From dbb22d8262fe729c320511e7b0201ed77626fe03 Mon Sep 17 00:00:00 2001 From: "iap10@labyrinth.cl.cam.ac.uk" Date: Mon, 22 Nov 2004 22:57:05 +0000 Subject: [PATCH] bitkeeper revision 1.1159.187.1 (41a26ec1W-iw8iKd-EuzGrvNLX-08g) Initial ia64 checkin. --- .rootkeys | 5 + xen/arch/ia64/privop.c | 863 ++++++++++++++++++++ xen/arch/ia64/process.c | 836 ++++++++++++++++++++ xen/arch/ia64/regionreg.c | 399 ++++++++++ xen/arch/ia64/vcpu.c | 1559 +++++++++++++++++++++++++++++++++++++ xen/arch/ia64/xenasm.S | 461 +++++++++++ 6 files changed, 4123 insertions(+) create mode 100644 xen/arch/ia64/privop.c create mode 100644 xen/arch/ia64/process.c create mode 100644 xen/arch/ia64/regionreg.c create mode 100644 xen/arch/ia64/vcpu.c create mode 100644 xen/arch/ia64/xenasm.S diff --git a/.rootkeys b/.rootkeys index 4c1e417632..493a8f6933 100644 --- a/.rootkeys +++ b/.rootkeys @@ -634,6 +634,11 @@ 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk +41a26ebcqaSGVQ8qTMwpPwOJSJ7qSw xen/arch/ia64/privop.c +41a26ebc4BOHDUsT0TSnryPeV2xfRA xen/arch/ia64/process.c +41a26ebcJ30TFl1v2kR8rqpEBvOtVw xen/arch/ia64/regionreg.c +41a26ebc--sjlYZQxmIxyCx3jw70qA xen/arch/ia64/vcpu.c +41a26ebc4jSBGQOuyNIPDST58mNbBw xen/arch/ia64/xenasm.S 3ddb79bcZbRBzT3elFWSX7u6NtMagQ xen/arch/x86/Makefile 3ddb79bcBQF85CfLS4i1WGZ4oLLaCA xen/arch/x86/Rules.mk 3e5636e5FAYZ5_vQnmgwFJfSdmO5Mw xen/arch/x86/acpi.c diff --git a/xen/arch/ia64/privop.c b/xen/arch/ia64/privop.c new file mode 100644 index 0000000000..0dfa9c164b --- /dev/null +++ b/xen/arch/ia64/privop.c @@ -0,0 +1,863 @@ +/* + * Privileged operation "API" handling functions. + * + * Copyright (C) 2004 Hewlett-Packard Co. + * Dan Magenheimer (dan.magenheimer@hp.com) + * + */ + +#include +#include +#include +#include +#include // Debug only +//#include + +long priv_verbose=0; + +/************************************************************************** +Hypercall bundle creation +**************************************************************************/ + + +void build_hypercall_bundle(UINT64 *imva, UINT64 breakimm, UINT64 hypnum, UINT64 ret) +{ + INST64_A5 slot0; + INST64_I19 slot1; + INST64_B4 slot2; + IA64_BUNDLE bundle; + + // slot1: mov r2 = hypnum (low 20 bits) + slot0.inst = 0; + slot0.qp = 0; slot0.r1 = 2; slot0.r3 = 0; slot0.major = 0x9; + slot0.imm7b = hypnum; slot0.imm9d = hypnum >> 7; + slot0.imm5c = hypnum >> 16; slot0.s = 0; + // slot1: break breakimm + slot1.inst = 0; + slot1.qp = 0; slot1.x6 = 0; slot1.x3 = 0; slot1.major = 0x0; + slot1.imm20 = breakimm; slot1.i = breakimm >> 20; + // if ret slot2: br.ret.sptk.many rp + // else slot2: br.cond.sptk.many rp + slot2.inst = 0; slot2.qp = 0; slot2.p = 1; slot2.b2 = 0; + slot2.wh = 0; slot2.d = 0; slot2.major = 0x0; + if (ret) { + slot2.btype = 4; slot2.x6 = 0x21; + } + else { + slot2.btype = 0; slot2.x6 = 0x20; + } + + bundle.i64[0] = 0; bundle.i64[1] = 0; + bundle.template = 0x11; + bundle.slot0 = slot0.inst; bundle.slot2 = slot2.inst; + bundle.slot1a = slot1.inst; bundle.slot1b = slot1.inst >> 18; + + *imva++ = bundle.i64[0]; *imva = bundle.i64[1]; +} + +/************************************************************************** +Privileged operation emulation routines +**************************************************************************/ + +IA64FAULT priv_rfi(VCPU *vcpu, INST64 inst) +{ + return vcpu_rfi(vcpu); +} + +IA64FAULT priv_bsw0(VCPU *vcpu, INST64 inst) +{ + return vcpu_bsw0(vcpu); +} + +IA64FAULT priv_bsw1(VCPU *vcpu, INST64 inst) +{ + return vcpu_bsw1(vcpu); +} + +IA64FAULT priv_cover(VCPU *vcpu, INST64 inst) +{ + return vcpu_cover(vcpu); +} + +IA64FAULT priv_ptc_l(VCPU *vcpu, INST64 inst) +{ + UINT64 vadr = vcpu_get_gr(vcpu,inst.M45.r3); + UINT64 addr_range; + + addr_range = 1 << ((vcpu_get_gr(vcpu,inst.M45.r2) & 0xfc) >> 2); + return vcpu_ptc_l(vcpu,vadr,addr_range); +} + +IA64FAULT priv_ptc_e(VCPU *vcpu, INST64 inst) +{ + UINT src = inst.M28.r3; + + // NOTE: ptc_e with source gr > 63 is emulated as a fc r(y-64) + if (src > 63) return(vcpu_fc(vcpu,vcpu_get_gr(vcpu,src - 64))); + return vcpu_ptc_e(vcpu,vcpu_get_gr(vcpu,src)); +} + +IA64FAULT priv_ptc_g(VCPU *vcpu, INST64 inst) +{ + UINT64 vadr = vcpu_get_gr(vcpu,inst.M45.r3); + UINT64 addr_range; + + addr_range = 1 << ((vcpu_get_gr(vcpu,inst.M45.r2) & 0xfc) >> 2); + return vcpu_ptc_g(vcpu,vadr,addr_range); +} + +IA64FAULT priv_ptc_ga(VCPU *vcpu, INST64 inst) +{ + UINT64 vadr = vcpu_get_gr(vcpu,inst.M45.r3); + UINT64 addr_range; + + addr_range = 1 << ((vcpu_get_gr(vcpu,inst.M45.r2) & 0xfc) >> 2); + return vcpu_ptc_ga(vcpu,vadr,addr_range); +} + +IA64FAULT priv_ptr_d(VCPU *vcpu, INST64 inst) +{ + UINT64 vadr = vcpu_get_gr(vcpu,inst.M45.r3); + UINT64 addr_range; + + addr_range = 1 << ((vcpu_get_gr(vcpu,inst.M45.r2) & 0xfc) >> 2); + return vcpu_ptr_d(vcpu,vadr,addr_range); +} + +IA64FAULT priv_ptr_i(VCPU *vcpu, INST64 inst) +{ + UINT64 vadr = vcpu_get_gr(vcpu,inst.M45.r3); + UINT64 addr_range; + + addr_range = 1 << ((vcpu_get_gr(vcpu,inst.M45.r2) & 0xfc) >> 2); + return vcpu_ptr_i(vcpu,vadr,addr_range); +} + +IA64FAULT priv_tpa(VCPU *vcpu, INST64 inst) +{ + UINT64 padr; + UINT fault; + UINT src = inst.M46.r3; + + // NOTE: tpa with source gr > 63 is emulated as a ttag rx=r(y-64) + if (src > 63) + fault = vcpu_ttag(vcpu,vcpu_get_gr(vcpu,src-64),&padr); + else fault = vcpu_tpa(vcpu,vcpu_get_gr(vcpu,src),&padr); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M46.r1, padr); + else return fault; +} + +IA64FAULT priv_tak(VCPU *vcpu, INST64 inst) +{ + UINT64 key; + UINT fault; + UINT src = inst.M46.r3; + + // NOTE: tak with source gr > 63 is emulated as a thash rx=r(y-64) + if (src > 63) + fault = vcpu_thash(vcpu,vcpu_get_gr(vcpu,src-64),&key); + else fault = vcpu_tak(vcpu,vcpu_get_gr(vcpu,src),&key); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M46.r1, key); + else return fault; +} + +/************************************ + * Insert translation register/cache +************************************/ + +IA64FAULT priv_itr_d(VCPU *vcpu, INST64 inst) +{ + UINT64 fault, itir, ifa, pte, slot; + + //if (!vcpu_get_psr_ic(vcpu)) return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_itir(vcpu,&itir)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_ifa(vcpu,&ifa)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + pte = vcpu_get_gr(vcpu,inst.M42.r2); + slot = vcpu_get_gr(vcpu,inst.M42.r3); + + return (vcpu_itr_d(vcpu,slot,pte,itir,ifa)); +} + +IA64FAULT priv_itr_i(VCPU *vcpu, INST64 inst) +{ + UINT64 fault, itir, ifa, pte, slot; + + //if (!vcpu_get_psr_ic(vcpu)) return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_itir(vcpu,&itir)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_ifa(vcpu,&ifa)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + pte = vcpu_get_gr(vcpu,inst.M42.r2); + slot = vcpu_get_gr(vcpu,inst.M42.r3); + + return (vcpu_itr_i(vcpu,slot,pte,itir,ifa)); +} + +IA64FAULT priv_itc_d(VCPU *vcpu, INST64 inst) +{ + UINT64 fault, itir, ifa, pte; + + //if (!vcpu_get_psr_ic(vcpu)) return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_itir(vcpu,&itir)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_ifa(vcpu,&ifa)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + pte = vcpu_get_gr(vcpu,inst.M41.r2); + + return (vcpu_itc_d(vcpu,pte,itir,ifa)); +} + +IA64FAULT priv_itc_i(VCPU *vcpu, INST64 inst) +{ + UINT64 fault, itir, ifa, pte; + + //if (!vcpu_get_psr_ic(vcpu)) return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_itir(vcpu,&itir)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + if ((fault = vcpu_get_ifa(vcpu,&ifa)) != IA64_NO_FAULT) + return(IA64_ILLOP_FAULT); + pte = vcpu_get_gr(vcpu,inst.M41.r2); + + return (vcpu_itc_i(vcpu,pte,itir,ifa)); +} + +/************************************* + * Moves to semi-privileged registers +*************************************/ + +IA64FAULT priv_mov_to_ar_imm(VCPU *vcpu, INST64 inst) +{ + // I27 and M30 are identical for these fields + UINT64 ar3 = inst.M30.ar3; + UINT64 imm = vcpu_get_gr(vcpu,inst.M30.imm); + return (vcpu_set_ar(vcpu,ar3,imm)); +} + +IA64FAULT priv_mov_to_ar_reg(VCPU *vcpu, INST64 inst) +{ + // I26 and M29 are identical for these fields + UINT64 ar3 = inst.M29.ar3; + + if (inst.M29.r2 > 63 && inst.M29.ar3 < 8) { // privified mov from kr + UINT64 val; + if (vcpu_get_ar(vcpu,ar3,&val) != IA64_ILLOP_FAULT) + return vcpu_set_gr(vcpu, inst.M29.r2-64, val); + else return IA64_ILLOP_FAULT; + } + else { + UINT64 r2 = vcpu_get_gr(vcpu,inst.M29.r2); + return (vcpu_set_ar(vcpu,ar3,r2)); + } +} + +/******************************** + * Moves to privileged registers +********************************/ + +IA64FAULT priv_mov_to_pkr(VCPU *vcpu, INST64 inst) +{ + UINT64 r3 = vcpu_get_gr(vcpu,inst.M42.r3); + UINT64 r2 = vcpu_get_gr(vcpu,inst.M42.r2); + return (vcpu_set_pkr(vcpu,r3,r2)); +} + +IA64FAULT priv_mov_to_rr(VCPU *vcpu, INST64 inst) +{ + UINT64 r3 = vcpu_get_gr(vcpu,inst.M42.r3); + UINT64 r2 = vcpu_get_gr(vcpu,inst.M42.r2); + return (vcpu_set_rr(vcpu,r3,r2)); +} + +IA64FAULT priv_mov_to_dbr(VCPU *vcpu, INST64 inst) +{ + UINT64 r3 = vcpu_get_gr(vcpu,inst.M42.r3); + UINT64 r2 = vcpu_get_gr(vcpu,inst.M42.r2); + return (vcpu_set_dbr(vcpu,r3,r2)); +} + +IA64FAULT priv_mov_to_ibr(VCPU *vcpu, INST64 inst) +{ + UINT64 r3 = vcpu_get_gr(vcpu,inst.M42.r3); + UINT64 r2 = vcpu_get_gr(vcpu,inst.M42.r2); + return (vcpu_set_ibr(vcpu,r3,r2)); +} + +IA64FAULT priv_mov_to_pmc(VCPU *vcpu, INST64 inst) +{ + UINT64 r3 = vcpu_get_gr(vcpu,inst.M42.r3); + UINT64 r2 = vcpu_get_gr(vcpu,inst.M42.r2); + return (vcpu_set_pmc(vcpu,r3,r2)); +} + +IA64FAULT priv_mov_to_pmd(VCPU *vcpu, INST64 inst) +{ + UINT64 r3 = vcpu_get_gr(vcpu,inst.M42.r3); + UINT64 r2 = vcpu_get_gr(vcpu,inst.M42.r2); + return (vcpu_set_pmd(vcpu,r3,r2)); +} + +unsigned long to_cr_cnt[128] = { 0 }; + +IA64FAULT priv_mov_to_cr(VCPU *vcpu, INST64 inst) +{ + UINT64 val = vcpu_get_gr(vcpu, inst.M32.r2); + to_cr_cnt[inst.M32.cr3]++; + switch (inst.M32.cr3) { + case 0: return vcpu_set_dcr(vcpu,val); + case 1: return vcpu_set_itm(vcpu,val); + case 2: return vcpu_set_iva(vcpu,val); + case 8: return vcpu_set_pta(vcpu,val); + case 16:return vcpu_set_ipsr(vcpu,val); + case 17:return vcpu_set_isr(vcpu,val); + case 19:return vcpu_set_iip(vcpu,val); + case 20:return vcpu_set_ifa(vcpu,val); + case 21:return vcpu_set_itir(vcpu,val); + case 22:return vcpu_set_iipa(vcpu,val); + case 23:return vcpu_set_ifs(vcpu,val); + case 24:return vcpu_set_iim(vcpu,val); + case 25:return vcpu_set_iha(vcpu,val); + case 64:return vcpu_set_lid(vcpu,val); + case 65:return IA64_ILLOP_FAULT; + case 66:return vcpu_set_tpr(vcpu,val); + case 67:return vcpu_set_eoi(vcpu,val); + case 68:return IA64_ILLOP_FAULT; + case 69:return IA64_ILLOP_FAULT; + case 70:return IA64_ILLOP_FAULT; + case 71:return IA64_ILLOP_FAULT; + case 72:return vcpu_set_itv(vcpu,val); + case 73:return vcpu_set_pmv(vcpu,val); + case 74:return vcpu_set_cmcv(vcpu,val); + case 80:return vcpu_set_lrr0(vcpu,val); + case 81:return vcpu_set_lrr1(vcpu,val); + default: return IA64_ILLOP_FAULT; + } +} + +IA64FAULT priv_rsm(VCPU *vcpu, INST64 inst) +{ + UINT64 imm24 = (inst.M44.i<<23)|(inst.M44.i2<<21)|inst.M44.imm; + return vcpu_reset_psr_sm(vcpu,imm24); +} + +IA64FAULT priv_ssm(VCPU *vcpu, INST64 inst) +{ + UINT64 imm24 = (inst.M44.i<<23)|(inst.M44.i2<<21)|inst.M44.imm; + return vcpu_set_psr_sm(vcpu,imm24); +} + +/** + * @todo Check for reserved bits and return IA64_RSVDREG_FAULT. + */ +IA64FAULT priv_mov_to_psr(VCPU *vcpu, INST64 inst) +{ + UINT64 val = vcpu_get_gr(vcpu, inst.M35.r2); + return vcpu_set_psr_l(vcpu,val); +} + +/********************************** + * Moves from privileged registers + **********************************/ + +IA64FAULT priv_mov_from_rr(VCPU *vcpu, INST64 inst) +{ + UINT64 val; + IA64FAULT fault; + + if (inst.M43.r1 > 63) { // privified mov from cpuid + fault = vcpu_get_cpuid(vcpu,vcpu_get_gr(vcpu,inst.M43.r3),&val); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M43.r1-64, val); + } + else { + fault = vcpu_get_rr(vcpu,vcpu_get_gr(vcpu,inst.M43.r3),&val); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M43.r1, val); + } + return fault; +} + +IA64FAULT priv_mov_from_pkr(VCPU *vcpu, INST64 inst) +{ + UINT64 val; + IA64FAULT fault; + + fault = vcpu_get_pkr(vcpu,vcpu_get_gr(vcpu,inst.M43.r3),&val); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M43.r1, val); + else return fault; +} + +IA64FAULT priv_mov_from_dbr(VCPU *vcpu, INST64 inst) +{ + UINT64 val; + IA64FAULT fault; + + fault = vcpu_get_dbr(vcpu,vcpu_get_gr(vcpu,inst.M43.r3),&val); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M43.r1, val); + else return fault; +} + +IA64FAULT priv_mov_from_ibr(VCPU *vcpu, INST64 inst) +{ + UINT64 val; + IA64FAULT fault; + + fault = vcpu_get_ibr(vcpu,vcpu_get_gr(vcpu,inst.M43.r3),&val); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M43.r1, val); + else return fault; +} + +IA64FAULT priv_mov_from_pmc(VCPU *vcpu, INST64 inst) +{ + UINT64 val; + IA64FAULT fault; + + fault = vcpu_get_pmc(vcpu,vcpu_get_gr(vcpu,inst.M43.r3),&val); + if (fault == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, inst.M43.r1, val); + else return fault; +} + +unsigned long from_cr_cnt[128] = { 0 }; + +#define cr_get(cr) \ + ((fault = vcpu_get_##cr(vcpu,&val)) == IA64_NO_FAULT) ? \ + vcpu_set_gr(vcpu, tgt, val) : fault; + +IA64FAULT priv_mov_from_cr(VCPU *vcpu, INST64 inst) +{ + UINT64 tgt = inst.M33.r1; + UINT64 val; + IA64FAULT fault; + + from_cr_cnt[inst.M33.cr3]++; + switch (inst.M33.cr3) { + case 0: return cr_get(dcr); + case 1: return cr_get(itm); + case 2: return cr_get(iva); + case 8: return cr_get(pta); + case 16:return cr_get(ipsr); + case 17:return cr_get(isr); + case 19:return cr_get(iip); + case 20:return cr_get(ifa); + case 21:return cr_get(itir); + case 22:return cr_get(iipa); + case 23:return cr_get(ifs); + case 24:return cr_get(iim); + case 25:return cr_get(iha); + case 64:return cr_get(lid); + case 65:return cr_get(ivr); + case 66:return cr_get(tpr); + case 67:return vcpu_set_gr(vcpu,tgt,0L); + case 68:return cr_get(irr0); + case 69:return cr_get(irr1); + case 70:return cr_get(irr2); + case 71:return cr_get(irr3); + case 72:return cr_get(itv); + case 73:return cr_get(pmv); + case 74:return cr_get(cmcv); + case 80:return cr_get(lrr0); + case 81:return cr_get(lrr1); + default: return IA64_ILLOP_FAULT; + } + return IA64_ILLOP_FAULT; +} + +IA64FAULT priv_mov_from_psr(VCPU *vcpu, INST64 inst) +{ + UINT64 tgt = inst.M33.r1; + UINT64 val; + IA64FAULT fault; + + if ((fault = vcpu_get_psr(vcpu,&val)) == IA64_NO_FAULT) + return vcpu_set_gr(vcpu, tgt, val); + else return fault; +} + +/************************************************************************** +Privileged operation decode and dispatch routines +**************************************************************************/ + +IA64_SLOT_TYPE slot_types[0x20][3] = { + {M, I, I}, {M, I, I}, {M, I, I}, {M, I, I}, + {M, I, ILLEGAL}, {M, I, ILLEGAL}, + {ILLEGAL, ILLEGAL, ILLEGAL}, {ILLEGAL, ILLEGAL, ILLEGAL}, + {M, M, I}, {M, M, I}, {M, M, I}, {M, M, I}, + {M, F, I}, {M, F, I}, + {M, M, F}, {M, M, F}, + {M, I, B}, {M, I, B}, + {M, B, B}, {M, B, B}, + {ILLEGAL, ILLEGAL, ILLEGAL}, {ILLEGAL, ILLEGAL, ILLEGAL}, + {B, B, B}, {B, B, B}, + {M, M, B}, {M, M, B}, + {ILLEGAL, ILLEGAL, ILLEGAL}, {ILLEGAL, ILLEGAL, ILLEGAL}, + {M, F, B}, {M, F, B}, + {ILLEGAL, ILLEGAL, ILLEGAL}, {ILLEGAL, ILLEGAL, ILLEGAL} +}; + +// pointer to privileged emulation function +typedef IA64FAULT (*PPEFCN)(VCPU *vcpu, INST64 inst); + +PPEFCN Mpriv_funcs[64] = { + priv_mov_to_rr, priv_mov_to_dbr, priv_mov_to_ibr, priv_mov_to_pkr, + priv_mov_to_pmc, priv_mov_to_pmd, 0, 0, + 0, priv_ptc_l, priv_ptc_g, priv_ptc_ga, + priv_ptr_d, priv_ptr_i, priv_itr_d, priv_itr_i, + priv_mov_from_rr, priv_mov_from_dbr, priv_mov_from_ibr, priv_mov_from_pkr, + priv_mov_from_pmc, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, priv_tpa, priv_tak, + 0, 0, 0, 0, + priv_mov_from_cr, priv_mov_from_psr, 0, 0, + 0, 0, 0, 0, + priv_mov_to_cr, priv_mov_to_psr, priv_itc_d, priv_itc_i, + 0, 0, 0, 0, + priv_ptc_e, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +struct { + unsigned long mov_to_ar_imm; + unsigned long mov_to_ar_reg; + unsigned long ssm; + unsigned long rsm; + unsigned long rfi; + unsigned long bsw0; + unsigned long bsw1; + unsigned long cover; + unsigned long Mpriv_cnt[64]; +} privcnt = { 0 }; + +unsigned long privop_trace = 0; + +IA64FAULT +priv_handle_op(VCPU *vcpu, REGS *regs, int privlvl) +{ + IA64_BUNDLE bundle, __get_domain_bundle(UINT64); + int slot; + IA64_SLOT_TYPE slot_type; + INST64 inst; + PPEFCN pfunc; + unsigned long ipsr = regs->cr_ipsr; + UINT64 iip = regs->cr_iip; + int x6; + + // make a local copy of the bundle containing the privop +#if 1 + bundle = __get_domain_bundle(iip); + if (!bundle.i64[0] && !bundle.i64[1]) return IA64_RETRY; +#else +#ifdef AVOIDING_POSSIBLE_DOMAIN_TLB_MISS + //TODO: this needs to check for faults and behave accordingly + if (!vcpu_get_iip_bundle(&bundle)) return IA64_DTLB_FAULT; +#else +if (iip < 0x10000) { + printf("priv_handle_op: unlikely iip=%p,b0=%p\n",iip,regs->b0); + dummy(); +} + bundle = *(IA64_BUNDLE *)iip; +#endif +#endif +#if 0 + if (iip==0xa000000100001820) { + static int firstpagefault = 1; + if (firstpagefault) { + printf("*** First time to domain page fault!\n"); firstpagefault=0; + } + } +#endif + if (privop_trace) { + static long i = 400; + //if (i > 0) printf("privop @%p\n",iip); + if (i > 0) printf("priv_handle_op: @%p, itc=%lx, itm=%lx\n", + iip,ia64_get_itc(),ia64_get_itm()); + i--; + } + slot = ((struct ia64_psr *)&ipsr)->ri; + if (!slot) inst.inst = (bundle.i64[0]>>5) & MASK_41; + else if (slot == 1) + inst.inst = ((bundle.i64[0]>>46) | bundle.i64[1]<<18) & MASK_41; + else if (slot == 2) inst.inst = (bundle.i64[1]>>23) & MASK_41; + else printf("priv_handle_op: illegal slot: %d\n", slot); + + slot_type = slot_types[bundle.template][slot]; + if (priv_verbose) { + printf("priv_handle_op: checking bundle at 0x%lx (op=0x%016lx) slot %d (type=%d)\n", + iip, (UINT64)inst.inst, slot, slot_type); + } + if (slot_type == B && inst.generic.major == 0 && inst.B8.x6 == 0x0) { + // break instr for privified cover + } + else if (privlvl != 2) return (IA64_ILLOP_FAULT); + switch (slot_type) { + case M: + if (inst.generic.major == 0) { +#if 0 + if (inst.M29.x6 == 0 && inst.M29.x3 == 0) { + privcnt.cover++; + return priv_cover(vcpu,inst); + } +#endif + if (inst.M29.x3 != 0) break; + if (inst.M30.x4 == 8 && inst.M30.x2 == 2) { + privcnt.mov_to_ar_imm++; + return priv_mov_to_ar_imm(vcpu,inst); + } + if (inst.M44.x4 == 6) { + privcnt.ssm++; + return priv_ssm(vcpu,inst); + } + if (inst.M44.x4 == 7) { + privcnt.rsm++; + return priv_rsm(vcpu,inst); + } + break; + } + else if (inst.generic.major != 1) break; + x6 = inst.M29.x6; + if (x6 == 0x2a) { + privcnt.mov_to_ar_reg++; + return priv_mov_to_ar_reg(vcpu,inst); + } + if (inst.M29.x3 != 0) break; + if (!(pfunc = Mpriv_funcs[x6])) break; + if (x6 == 0x1e || x6 == 0x1f) { // tpa or tak are "special" + if (inst.M46.r3 > 63) { + if (x6 == 0x1e) x6 = 0x1b; + else x6 = 0x1a; + } + } + privcnt.Mpriv_cnt[x6]++; + return (*pfunc)(vcpu,inst); + break; + case B: + if (inst.generic.major != 0) break; + if (inst.B8.x6 == 0x08) { + IA64FAULT fault; + privcnt.rfi++; + fault = priv_rfi(vcpu,inst); + if (fault == IA64_NO_FAULT) fault = IA64_RFI_IN_PROGRESS; + return fault; + } + if (inst.B8.x6 == 0x0c) { + privcnt.bsw0++; + return priv_bsw0(vcpu,inst); + } + if (inst.B8.x6 == 0x0d) { + privcnt.bsw1++; + return priv_bsw1(vcpu,inst); + } + if (inst.B8.x6 == 0x0) { // break instr for privified cover + privcnt.cover++; + return priv_cover(vcpu,inst); + } + break; + case I: + if (inst.generic.major != 0) break; +#if 0 + if (inst.I26.x6 == 0 && inst.I26.x3 == 0) { + privcnt.cover++; + return priv_cover(vcpu,inst); + } +#endif + if (inst.I26.x3 != 0) break; // I26.x3 == I27.x3 + if (inst.I26.x6 == 0x2a) { + privcnt.mov_to_ar_reg++; + return priv_mov_to_ar_reg(vcpu,inst); + } + if (inst.I27.x6 == 0x0a) { + privcnt.mov_to_ar_imm++; + return priv_mov_to_ar_imm(vcpu,inst); + } + break; + default: + break; + } + //printf("We who are about do die salute you\n"); + printf("handle_op: can't handle privop at 0x%lx (op=0x%016lx) slot %d (type=%d)\n", + iip, (UINT64)inst.inst, slot, slot_type); + //printf("vtop(0x%lx)==0x%lx\r\n", iip, tr_vtop(iip)); + //thread_mozambique("privop fault\n"); + return (IA64_ILLOP_FAULT); +} + +/** Emulate a privileged operation. + * + * This should probably return 0 on success and the "trap number" + * (e.g. illegal operation for bad register, priv op for an + * instruction that isn't allowed, etc.) on "failure" + * + * @param vcpu virtual cpu + * @param isrcode interrupt service routine code + * @return fault + */ +IA64FAULT +priv_emulate(VCPU *vcpu, REGS *regs, UINT64 isr) +{ + IA64FAULT fault; + UINT64 ipsr = regs->cr_ipsr; + UINT64 isrcode = (isr >> 4) & 0xf; + int privlvl; + + // handle privops masked as illops? and breaks (6) + if (isrcode != 1 && isrcode != 2 && isrcode != 0 && isrcode != 6) { + printf("priv_emulate: isrcode != 0 or 1 or 2\n"); + printf("priv_emulate: returning ILLOP, not implemented!\n"); + while (1); + return IA64_ILLOP_FAULT; + } + //if (isrcode != 1 && isrcode != 2) return 0; + vcpu_set_regs(vcpu,regs); + privlvl = (ipsr & IA64_PSR_CPL) >> IA64_PSR_CPL0_BIT; + // its OK for a privified-cover to be executed in user-land + fault = priv_handle_op(vcpu,regs,privlvl); + if (fault == IA64_NO_FAULT) { // success!! + // update iip/ipsr to point to the next instruction + (void)vcpu_increment_iip(vcpu); + } + else if (fault == IA64_EXTINT_VECTOR) { + // update iip/ipsr before delivering interrupt + (void)vcpu_increment_iip(vcpu); + } + else if (fault == IA64_RFI_IN_PROGRESS) return fault; + // success but don't update to next instruction + else if (fault == IA64_RETRY) { + //printf("Priv emulate gets IA64_RETRY\n"); + //printf("priv_emulate: returning RETRY, not implemented!\n"); + //while (1); + // don't update iip/ipsr, deliver + + vcpu_force_data_miss(vcpu,regs->cr_iip); + return IA64_RETRY; + } + else if (priv_verbose) printf("unhandled operation from handle_op\n"); +// if (fault == IA64_ILLOP_FAULT) { +// printf("priv_emulate: returning ILLOP, not implemented!\n"); +// while (1); +// } + return fault; +} + + +/************************************************************************** +Privileged operation instrumentation routines +**************************************************************************/ + +char *Mpriv_str[64] = { + "mov_to_rr", "mov_to_dbr", "mov_to_ibr", "mov_to_pkr", + "mov_to_pmc", "mov_to_pmd", "<0x06>", "<0x07>", + "<0x08>", "ptc_l", "ptc_g", "ptc_ga", + "ptr_d", "ptr_i", "itr_d", "itr_i", + "mov_from_rr", "mov_from_dbr", "mov_from_ibr", "mov_from_pkr", + "mov_from_pmc", "<0x15>", "<0x16>", "<0x17>", + "<0x18>", "<0x19>", "privified-thash", "privified-ttag", + "<0x1c>", "<0x1d>", "tpa", "tak", + "<0x20>", "<0x21>", "<0x22>", "<0x23>", + "mov_from_cr", "mov_from_psr", "<0x26>", "<0x27>", + "<0x28>", "<0x29>", "<0x2a>", "<0x2b>", + "mov_to_cr", "mov_to_psr", "itc_d", "itc_i", + "<0x30>", "<0x31>", "<0x32>", "<0x33>", + "ptc_e", "<0x35>", "<0x36>", "<0x37>", + "<0x38>", "<0x39>", "<0x3a>", "<0x3b>", + "<0x3c>", "<0x3d>", "<0x3e>", "<0x3f>" +}; + +#define RS "Rsvd" +char *cr_str[128] = { + "dcr","itm","iva",RS,RS,RS,RS,RS, + "pta",RS,RS,RS,RS,RS,RS,RS, + "ipsr","isr",RS,"iip","ifa","itir","iipa","ifs", + "iim","iha",RS,RS,RS,RS,RS,RS, + RS,RS,RS,RS,RS,RS,RS,RS, RS,RS,RS,RS,RS,RS,RS,RS, + RS,RS,RS,RS,RS,RS,RS,RS, RS,RS,RS,RS,RS,RS,RS,RS, + "lid","ivr","tpr","eoi","irr0","irr1","irr2","irr3", + "itv","pmv","cmcv",RS,RS,RS,RS,RS, + "lrr0","lrr1",RS,RS,RS,RS,RS,RS, + RS,RS,RS,RS,RS,RS,RS,RS, RS,RS,RS,RS,RS,RS,RS,RS, + RS,RS,RS,RS,RS,RS,RS,RS, RS,RS,RS,RS,RS,RS,RS,RS, + RS,RS,RS,RS,RS,RS,RS,RS +}; + +void dump_privop_counts(void) +{ + int i, j; + UINT64 sum = 0; + + // this is ugly and should probably produce sorted output + // but it will have to do for now + sum += privcnt.mov_to_ar_imm; sum += privcnt.mov_to_ar_reg; + sum += privcnt.ssm; sum += privcnt.rsm; + sum += privcnt.rfi; sum += privcnt.bsw0; + sum += privcnt.bsw1; sum += privcnt.cover; + for (i=0; i < 64; i++) sum += privcnt.Mpriv_cnt[i]; + printf("Privop statistics: (Total privops: %ld)\r\n",sum); + if (privcnt.mov_to_ar_imm) + printf("%10d %s [%d%%]\r\n", privcnt.mov_to_ar_imm, + "mov_to_ar_imm", (privcnt.mov_to_ar_imm*100L)/sum); + if (privcnt.mov_to_ar_reg) + printf("%10d %s [%d%%]\r\n", privcnt.mov_to_ar_reg, + "mov_to_ar_reg", (privcnt.mov_to_ar_reg*100L)/sum); + if (privcnt.ssm) + printf("%10d %s [%d%%]\r\n", privcnt.ssm, + "ssm", (privcnt.ssm*100L)/sum); + if (privcnt.rsm) + printf("%10d %s [%d%%]\r\n", privcnt.rsm, + "rsm", (privcnt.rsm*100L)/sum); + if (privcnt.rfi) + printf("%10d %s [%d%%]\r\n", privcnt.rfi, + "rfi", (privcnt.rfi*100L)/sum); + if (privcnt.bsw0) + printf("%10d %s [%d%%]\r\n", privcnt.bsw0, + "bsw0", (privcnt.bsw0*100L)/sum); + if (privcnt.bsw1) + printf("%10d %s [%d%%]\r\n", privcnt.bsw1, + "bsw1", (privcnt.bsw1*100L)/sum); + if (privcnt.cover) + printf("%10d %s [%d%%]\r\n", privcnt.cover, + "cover", (privcnt.cover*100L)/sum); + for (i=0; i < 64; i++) if (privcnt.Mpriv_cnt[i]) { + if (!Mpriv_str[i]) printf("PRIVSTRING NULL!!\r\n"); + else printf("%10d %s [%d%%]\r\n", privcnt.Mpriv_cnt[i], + Mpriv_str[i], (privcnt.Mpriv_cnt[i]*100L)/sum); + if (i == 0x24) { // mov from CR + printf(" ["); + for (j=0; j < 128; j++) if (from_cr_cnt[j]) { + if (!cr_str[j]) + printf("PRIVSTRING NULL!!\r\n"); + printf("%s(%d),",cr_str[j],from_cr_cnt[j]); + } + printf("]\r\n"); + } + else if (i == 0x2c) { // mov to CR + printf(" ["); + for (j=0; j < 128; j++) if (to_cr_cnt[j]) { + if (!cr_str[j]) + printf("PRIVSTRING NULL!!\r\n"); + printf("%s(%d),",cr_str[j],to_cr_cnt[j]); + } + printf("]\r\n"); + } + } +} + +void zero_privop_counts(void) +{ + int i, j; + + // this is ugly and should probably produce sorted output + // but it will have to do for now + printf("Zeroing privop statistics\r\n"); + privcnt.mov_to_ar_imm = 0; privcnt.mov_to_ar_reg = 0; + privcnt.ssm = 0; privcnt.rsm = 0; + privcnt.rfi = 0; privcnt.bsw0 = 0; + privcnt.bsw1 = 0; privcnt.cover = 0; + for (i=0; i < 64; i++) privcnt.Mpriv_cnt[i] = 0; + for (j=0; j < 128; j++) from_cr_cnt[j] = 0; + for (j=0; j < 128; j++) to_cr_cnt[j] = 0; +} diff --git a/xen/arch/ia64/process.c b/xen/arch/ia64/process.c new file mode 100644 index 0000000000..9201446bce --- /dev/null +++ b/xen/arch/ia64/process.c @@ -0,0 +1,836 @@ +/* + * Miscellaneous process/domain related routines + * + * Copyright (C) 2004 Hewlett-Packard Co. + * Dan Magenheimer (dan.magenheimer@hp.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include /* FOR EFI_UNIMPLEMENTED */ +#include /* FOR struct ia64_sal_retval */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct ia64_sal_retval pal_emulator_static(UINT64); +extern struct ia64_sal_retval sal_emulator(UINT64,UINT64,UINT64,UINT64,UINT64,UINT64,UINT64,UINT64); + +extern unsigned long dom0_start, dom0_size; + +#define IA64_PSR_CPL1 (__IA64_UL(1) << IA64_PSR_CPL1_BIT) +// note IA64_PSR_PK removed from following, why is this necessary? +#define DELIVER_PSR_SET (IA64_PSR_IC | IA64_PSR_I | \ + IA64_PSR_DT | IA64_PSR_RT | IA64_PSR_CPL1 | \ + IA64_PSR_IT | IA64_PSR_BN) + +#define DELIVER_PSR_CLR (IA64_PSR_AC | IA64_PSR_DFL | IA64_PSR_DFH | \ + IA64_PSR_SP | IA64_PSR_DI | IA64_PSR_SI | \ + IA64_PSR_DB | IA64_PSR_LP | IA64_PSR_TB | \ + IA64_PSR_CPL | IA64_PSR_MC | IA64_PSR_IS | \ + IA64_PSR_ID | IA64_PSR_DA | IA64_PSR_DD | \ + IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | IA64_PSR_IA) + +#define PSCB(x) x->shared_info->arch + +extern unsigned long vcpu_verbose; + +long do_iopl(domid_t domain, unsigned int new_io_pl) +{ + dummy(); + return 0; +} + +void schedule_tail(struct domain *next) +{ + unsigned long rr7; + printk("current=%lx,shared_info=%lx\n",current,current->shared_info); + printk("next=%lx,shared_info=%lx\n",next,next->shared_info); + if (rr7 = load_region_regs(current)) { + printk("schedule_tail: change to rr7 not yet implemented\n"); + } +} + +extern TR_ENTRY *match_tr(struct domain *d, unsigned long ifa); + +void tdpfoo(void) { } + +// given a domain virtual address, pte and pagesize, extract the metaphysical +// address, convert the pte for a physical address for (possibly different) +// Xen PAGE_SIZE and return modified pte. (NOTE: TLB insert should use +// PAGE_SIZE!) +unsigned long translate_domain_pte(unsigned long pteval, + unsigned long address, unsigned long itir) +{ + struct domain *d = (struct domain *) current; + unsigned long mask, pteval2, mpaddr; + unsigned long lookup_domain_mpa(struct domain *,unsigned long); + extern struct domain *dom0; + extern unsigned long dom0_start, dom0_size; + + // FIXME address had better be pre-validated on insert + mask = (1L << ((itir >> 2) & 0x3f)) - 1; + mpaddr = ((pteval & _PAGE_PPN_MASK) & ~mask) | (address & mask); + if (d == dom0) { + if (mpaddr < dom0_start || mpaddr >= dom0_start + dom0_size) { + //printk("translate_domain_pte: out-of-bounds dom0 mpaddr %p! itc=%lx...\n",mpaddr,ia64_get_itc()); + tdpfoo(); + } + } + else if ((mpaddr >> PAGE_SHIFT) > d->max_pages) { + printf("translate_domain_pte: bad mpa=%p (> %p),vadr=%p,pteval=%p,itir=%p\n", + mpaddr,d->max_pages<2 (PL3 is unaffected) + pteval2 = (pteval & ~_PAGE_PPN_MASK) | pteval2; + return pteval2; +} + +// given a current domain metaphysical address, return the physical address +unsigned long translate_domain_mpaddr(unsigned long mpaddr) +{ + extern unsigned long lookup_domain_mpa(struct domain *,unsigned long); + unsigned long pteval; + + if (current == dom0) { + if (mpaddr < dom0_start || mpaddr >= dom0_start + dom0_size) { + printk("translate_domain_mpaddr: out-of-bounds dom0 mpaddr %p! continuing...\n",mpaddr); + tdpfoo(); + } + } + pteval = lookup_domain_mpa(current,mpaddr); + return ((pteval & _PAGE_PPN_MASK) | (mpaddr & ~PAGE_MASK)); +} + +void reflect_interruption(unsigned long ifa, unsigned long isr, unsigned long itiriim, struct pt_regs *regs, unsigned long vector) +{ + unsigned long vcpu_get_ipsr_int_state(struct domain *,unsigned long); + unsigned long vcpu_get_rr_ve(struct domain *,unsigned long); + unsigned long vcpu_get_itir_on_fault(struct domain *,unsigned long); + struct domain *d = (struct domain *) current; + + if (vector == IA64_EXTINT_VECTOR) { + + extern unsigned long vcpu_verbose, privop_trace; + static first_extint = 1; + if (first_extint) { + printf("Delivering first extint to domain: ifa=%p, isr=%p, itir=%p, iip=%p\n",ifa,isr,itiriim,regs->cr_iip); + //privop_trace = 1; vcpu_verbose = 1; + first_extint = 0; + } + } + if (!PSCB(d).interrupt_collection_enabled) { + if (!(PSCB(d).ipsr & IA64_PSR_DT)) { + printf("psr.dt off, trying to deliver nested dtlb!\n"); + while(1); + } + vector &= ~0xf; + if (vector != IA64_DATA_TLB_VECTOR && + vector != IA64_DATA_TLB_VECTOR) { +printf("psr.ic off, delivering fault=%lx,iip=%p,isr=%p,PSCB.iip=%p\n", + vector,regs->cr_iip,isr,PSCB(d).iip); + while(1); + + } +//printf("Delivering NESTED DATA TLB fault\n"); + vector = IA64_DATA_NESTED_TLB_VECTOR; + regs->cr_iip = ((unsigned long) PSCB(d).iva + vector) & ~0xffUL; + regs->cr_ipsr = (regs->cr_ipsr & ~DELIVER_PSR_CLR) | DELIVER_PSR_SET; +// NOTE: nested trap must NOT pass PSCB address + //regs->r31 = (unsigned long) &PSCB(d); + return; + + } + if ((vector & 0xf) != IA64_FORCED_IFA) PSCB(d).ifa = ifa; + else ifa = PSCB(d).ifa; + vector &= ~0xf; +// always deliver on ALT vector (for now?) because no VHPT +// if (!vcpu_get_rr_ve(d,ifa)) { + if (vector == IA64_DATA_TLB_VECTOR) + vector = IA64_ALT_DATA_TLB_VECTOR; + else if (vector == IA64_INST_TLB_VECTOR) + vector = IA64_ALT_INST_TLB_VECTOR; +// } + PSCB(d).unat = regs->ar_unat; // not sure if this is really needed? + PSCB(d).precover_ifs = regs->cr_ifs; + vcpu_bsw0(d); + PSCB(d).ipsr = vcpu_get_ipsr_int_state(d,regs->cr_ipsr); + if (vector == IA64_BREAK_VECTOR || vector == IA64_SPECULATION_VECTOR) + PSCB(d).iim = itiriim; + else PSCB(d).itir = vcpu_get_itir_on_fault(d,ifa); + PSCB(d).isr = isr; // this is unnecessary except for interrupts! + PSCB(d).iip = regs->cr_iip; + PSCB(d).ifs = 0; + PSCB(d).incomplete_regframe = 0; + + regs->cr_iip = ((unsigned long) PSCB(d).iva + vector) & ~0xffUL; + regs->cr_ipsr = (regs->cr_ipsr & ~DELIVER_PSR_CLR) | DELIVER_PSR_SET; +// FIXME: NEED TO PASS PSCB, BUT **NOT** IN R31 WHICH IS BEING USED FOR ar.pr +// IN ANY CASE, PASS PINNED ADDRESS, NOT THIS ONE + //regs->r31 = (unsigned long) &PSCB(d); + + PSCB(d).interrupt_delivery_enabled = 0; + PSCB(d).interrupt_collection_enabled = 0; +} + +void foodpi(void) {} + +// ONLY gets called from ia64_leave_kernel +// ONLY call with interrupts disabled?? (else might miss one?) +// NEVER successful if already reflecting a trap/fault because psr.i==0 +void deliver_pending_interrupt(struct pt_regs *regs) +{ + struct domain *d = (struct domain *) current; + // FIXME: Will this work properly if doing an RFI??? + if (!is_idle_task(d) && user_mode(regs)) { + vcpu_poke_timer(d); + if (vcpu_deliverable_interrupts(d)) { + unsigned long isr = regs->cr_ipsr & IA64_PSR_RI; + foodpi(); + reflect_interruption(0,isr,0,regs,IA64_EXTINT_VECTOR); + } + } +} + +int handle_lazy_cover(struct domain *d, unsigned long isr, struct pt_regs *regs) +{ + if (!PSCB(d).interrupt_collection_enabled) { + if (isr & IA64_ISR_IR) { +// printf("Handling lazy cover\n"); + PSCB(d).ifs = regs->cr_ifs; + PSCB(d).incomplete_regframe = 1; + regs->cr_ifs = 0; + return(1); // retry same instruction with cr.ifs off + } + } + return(0); +} + +#define IS_XEN_ADDRESS(d,a) ((a >= d->xen_vastart) && (a <= d->xen_vaend)) + +void xen_handle_domain_access(unsigned long address, unsigned long isr, struct pt_regs *regs, unsigned long itir) +{ + struct domain *d = (struct domain *) current; + TR_ENTRY *trp; + unsigned long psr = regs->cr_ipsr, mask, flags; + unsigned long iip = regs->cr_iip; + // FIXME should validate address here + unsigned long pteval, mpaddr; + unsigned long lookup_domain_mpa(struct domain *,unsigned long); + IA64FAULT fault; + extern void __get_domain_bundle(void); + +// NEED TO HANDLE THREE CASES: +// 1) domain is in metaphysical mode +// 2) domain address is in TR +// 3) domain address is not in TR (reflect data miss) + + // got here trying to read a privop bundle + //if (d->metaphysical_mode) { + if (d->metaphysical_mode && !(address>>61)) { //FIXME + if (d == dom0) { + if (address < dom0_start || address >= dom0_start + dom0_size) { + printk("xen_handle_domain_access: out-of-bounds" + "dom0 mpaddr %p! continuing...\n",mpaddr); + tdpfoo(); + } + } + pteval = lookup_domain_mpa(d,address); + //FIXME: check return value? + // would be nice to have a counter here + vcpu_itc_no_srlz(d,2,address,pteval,PAGE_SHIFT); + return; + } +if (address < 0x4000) printf("WARNING: page_fault @%p, iip=%p\n",address,iip); + if (*(unsigned long *)__get_domain_bundle != iip) { + printf("Bad user space access @%p ",address); + printf("iip=%p, ipsr=%p, b0=%p\n",iip,psr,regs->b0); + while(1); + } + + fault = vcpu_tpa(d,address,&mpaddr); + if (fault != IA64_NO_FAULT) { + // this is hardcoded to handle __get_domain_bundle only + regs->r8 = 0; regs->r9 = 0; + regs->cr_iip += 0x20; + //regs->cr_iip |= (2UL << IA64_PSR_RI_BIT); + return; + } + if (d == dom0) { + if (mpaddr < dom0_start || mpaddr >= dom0_start + dom0_size) { + printk("xen_handle_domain_access: vcpu_tpa returned out-of-bounds dom0 mpaddr %p! continuing...\n",mpaddr); + tdpfoo(); + } + } + pteval = lookup_domain_mpa(d,mpaddr); + // would be nice to have a counter here + //printf("Handling privop data TLB miss\n"); + // FIXME, must be inlined or potential for nested fault here! + vcpu_itc_no_srlz(d,2,address,pteval,PAGE_SHIFT); +} + +void ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *regs, unsigned long itir) +{ + struct domain *d = (struct domain *) current; + TR_ENTRY *trp; + unsigned long psr = regs->cr_ipsr, mask, flags; + unsigned long iip = regs->cr_iip; + // FIXME should validate address here + unsigned long pteval, mpaddr; + unsigned long lookup_domain_mpa(struct domain *,unsigned long); + unsigned long is_data = !((isr >> IA64_ISR_X_BIT) & 1UL); + unsigned long vector; + IA64FAULT fault; + + + //The right way is put in VHPT and take another miss! + + // weak attempt to avoid doing both I/D tlb insert to avoid + // problems for privop bundle fetch, doesn't work, deal with later + if (IS_XEN_ADDRESS(d,iip) && !IS_XEN_ADDRESS(d,address)) { + xen_handle_domain_access(address, isr, regs, itir); + + return; + } + + // FIXME: no need to pass itir in to this routine as we need to + // compute the virtual itir anyway (based on domain's RR.ps) + // AND ACTUALLY reflect_interruption doesn't use it anyway! + itir = vcpu_get_itir_on_fault(d,address); + + if (d->metaphysical_mode && (is_data || !(address>>61))) { //FIXME + // FIXME should validate mpaddr here + if (d == dom0) { + if (address < dom0_start || address >= dom0_start + dom0_size) { + printk("ia64_do_page_fault: out-of-bounds dom0 mpaddr %p, iip=%p! continuing...\n",address,iip); + printk("ia64_do_page_fault: out-of-bounds dom0 mpaddr %p, old iip=%p!\n",address,d->shared_info->arch.iip); + tdpfoo(); + } + } + pteval = lookup_domain_mpa(d,address); + // FIXME, must be inlined or potential for nested fault here! + vcpu_itc_no_srlz(d,is_data?2:1,address,pteval,PAGE_SHIFT); + return; + } + if (trp = match_tr(d,address)) { + // FIXME address had better be pre-validated on insert + pteval = translate_domain_pte(trp->page_flags,address,trp->itir); + vcpu_itc_no_srlz(d,is_data?2:1,address,pteval,(trp->itir>>2)&0x3f); + return; + } + vector = is_data ? IA64_DATA_TLB_VECTOR : IA64_INST_TLB_VECTOR; + if (handle_lazy_cover(d, isr, regs)) return; +if (!(address>>61)) { printf("ia64_do_page_fault: @%p???, iip=%p, itc=%p (spinning...)\n",address,iip,ia64_get_itc()); while(1); } + if ((isr & IA64_ISR_SP) + || ((isr & IA64_ISR_NA) && (isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH)) + { + /* + * This fault was due to a speculative load or lfetch.fault, set the "ed" + * bit in the psr to ensure forward progress. (Target register will get a + * NaT for ld.s, lfetch will be canceled.) + */ + ia64_psr(regs)->ed = 1; + return; + } + reflect_interruption(address, isr, itir, regs, vector); +} + +void +ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, + unsigned long iim, unsigned long itir, unsigned long arg5, + unsigned long arg6, unsigned long arg7, unsigned long stack) +{ + struct pt_regs *regs = (struct pt_regs *) &stack; + unsigned long code, error = isr; + char buf[128]; + int result, sig; + static const char *reason[] = { + "IA-64 Illegal Operation fault", + "IA-64 Privileged Operation fault", + "IA-64 Privileged Register fault", + "IA-64 Reserved Register/Field fault", + "Disabled Instruction Set Transition fault", + "Unknown fault 5", "Unknown fault 6", "Unknown fault 7", "Illegal Hazard fault", + "Unknown fault 9", "Unknown fault 10", "Unknown fault 11", "Unknown fault 12", + "Unknown fault 13", "Unknown fault 14", "Unknown fault 15" + }; +#if 0 +printf("ia64_fault, vector=0x%p, ifa=%p, iip=%p, ipsr=%p, isr=%p\n", + vector, ifa, regs->cr_iip, regs->cr_ipsr, isr); +#endif + + if ((isr & IA64_ISR_NA) && ((isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH)) { + /* + * This fault was due to lfetch.fault, set "ed" bit in the psr to cancel + * the lfetch. + */ + ia64_psr(regs)->ed = 1; + printf("ia64_fault: handled lfetch.fault\n"); + return; + } + + switch (vector) { + case 24: /* General Exception */ + code = (isr >> 4) & 0xf; + sprintf(buf, "General Exception: %s%s", reason[code], + (code == 3) ? ((isr & (1UL << 37)) + ? " (RSE access)" : " (data access)") : ""); + if (code == 8) { +# ifdef CONFIG_IA64_PRINT_HAZARDS + printk("%s[%d]: possible hazard @ ip=%016lx (pr = %016lx)\n", + current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri, + regs->pr); +# endif + printf("ia64_fault: returning on hazard\n"); + return; + } + break; + + case 25: /* Disabled FP-Register */ + if (isr & 2) { + //disabled_fph_fault(regs); + //return; + } + sprintf(buf, "Disabled FPL fault---not supposed to happen!"); + break; + + case 26: /* NaT Consumption */ + if (user_mode(regs)) { + void *addr; + + if (((isr >> 4) & 0xf) == 2) { + /* NaT page consumption */ + //sig = SIGSEGV; + //code = SEGV_ACCERR; + addr = (void *) ifa; + } else { + /* register NaT consumption */ + //sig = SIGILL; + //code = ILL_ILLOPN; + addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + } + //siginfo.si_signo = sig; + //siginfo.si_code = code; + //siginfo.si_errno = 0; + //siginfo.si_addr = addr; + //siginfo.si_imm = vector; + //siginfo.si_flags = __ISR_VALID; + //siginfo.si_isr = isr; + //force_sig_info(sig, &siginfo, current); + //return; + } //else if (ia64_done_with_exception(regs)) + //return; + sprintf(buf, "NaT consumption"); + break; + + case 31: /* Unsupported Data Reference */ + if (user_mode(regs)) { + //siginfo.si_signo = SIGILL; + //siginfo.si_code = ILL_ILLOPN; + //siginfo.si_errno = 0; + //siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + //siginfo.si_imm = vector; + //siginfo.si_flags = __ISR_VALID; + //siginfo.si_isr = isr; + //force_sig_info(SIGILL, &siginfo, current); + //return; + } + sprintf(buf, "Unsupported data reference"); + break; + + case 29: /* Debug */ + case 35: /* Taken Branch Trap */ + case 36: /* Single Step Trap */ + //if (fsys_mode(current, regs)) {} + switch (vector) { + case 29: + //siginfo.si_code = TRAP_HWBKPT; +#ifdef CONFIG_ITANIUM + /* + * Erratum 10 (IFA may contain incorrect address) now has + * "NoFix" status. There are no plans for fixing this. + */ + if (ia64_psr(regs)->is == 0) + ifa = regs->cr_iip; +#endif + break; + case 35: ifa = 0; break; + case 36: ifa = 0; break; + //case 35: siginfo.si_code = TRAP_BRANCH; ifa = 0; break; + //case 36: siginfo.si_code = TRAP_TRACE; ifa = 0; break; + } + //siginfo.si_signo = SIGTRAP; + //siginfo.si_errno = 0; + //siginfo.si_addr = (void *) ifa; + //siginfo.si_imm = 0; + //siginfo.si_flags = __ISR_VALID; + //siginfo.si_isr = isr; + //force_sig_info(SIGTRAP, &siginfo, current); + //return; + + case 32: /* fp fault */ + case 33: /* fp trap */ + //result = handle_fpu_swa((vector == 32) ? 1 : 0, regs, isr); + if ((result < 0) || (current->thread.flags & IA64_THREAD_FPEMU_SIGFPE)) { + //siginfo.si_signo = SIGFPE; + //siginfo.si_errno = 0; + //siginfo.si_code = FPE_FLTINV; + //siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + //siginfo.si_flags = __ISR_VALID; + //siginfo.si_isr = isr; + //siginfo.si_imm = 0; + //force_sig_info(SIGFPE, &siginfo, current); + } + //return; + sprintf(buf, "FP fault/trap"); + break; + + case 34: + if (isr & 0x2) { + /* Lower-Privilege Transfer Trap */ + /* + * Just clear PSR.lp and then return immediately: all the + * interesting work (e.g., signal delivery is done in the kernel + * exit path). + */ + //ia64_psr(regs)->lp = 0; + //return; + sprintf(buf, "Lower-Privilege Transfer trap"); + } else { + /* Unimplemented Instr. Address Trap */ + if (user_mode(regs)) { + //siginfo.si_signo = SIGILL; + //siginfo.si_code = ILL_BADIADDR; + //siginfo.si_errno = 0; + //siginfo.si_flags = 0; + //siginfo.si_isr = 0; + //siginfo.si_imm = 0; + //siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + //force_sig_info(SIGILL, &siginfo, current); + //return; + } + sprintf(buf, "Unimplemented Instruction Address fault"); + } + break; + + case 45: + printk(KERN_ERR "Unexpected IA-32 exception (Trap 45)\n"); + printk(KERN_ERR " iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx\n", + regs->cr_iip, ifa, isr); + //force_sig(SIGSEGV, current); + break; + + case 46: + printk(KERN_ERR "Unexpected IA-32 intercept trap (Trap 46)\n"); + printk(KERN_ERR " iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx, iim - 0x%lx\n", + regs->cr_iip, ifa, isr, iim); + //force_sig(SIGSEGV, current); + return; + + case 47: + sprintf(buf, "IA-32 Interruption Fault (int 0x%lx)", isr >> 16); + break; + + default: + sprintf(buf, "Fault %lu", vector); + break; + } + //die_if_kernel(buf, regs, error); +printk("ia64_fault: %s: reflecting\n",buf); +reflect_interruption(ifa,isr,iim,regs,IA64_GENEX_VECTOR); +//while(1); + //force_sig(SIGILL, current); +} + +unsigned long running_on_sim = 0; + +void +do_ssc(unsigned long ssc, struct pt_regs *regs) +{ + extern unsigned long lookup_domain_mpa(struct domain *,unsigned long); + unsigned long arg0, arg1, arg2, arg3, retval; + char buf[2]; +/**/ static int last_fd, last_count; // FIXME FIXME FIXME +/**/ // BROKEN FOR MULTIPLE DOMAINS & SMP +/**/ struct ssc_disk_stat { int fd; unsigned count;} *stat, last_stat; + extern unsigned long vcpu_verbose, privop_trace; + + arg0 = vcpu_get_gr(current,32); + switch(ssc) { + case SSC_PUTCHAR: + buf[0] = arg0; + buf[1] = '\0'; + printf(buf); + break; + case SSC_GETCHAR: + retval = ia64_ssc(0,0,0,0,ssc); + vcpu_set_gr(current,8,retval); + break; + case SSC_WAIT_COMPLETION: + if (arg0) { // metaphysical address + + arg0 = translate_domain_mpaddr(arg0); +/**/ stat = (struct ssc_disk_stat *)__va(arg0); +///**/ if (stat->fd == last_fd) stat->count = last_count; +/**/ stat->count = last_count; +//if (last_count >= PAGE_SIZE) printf("ssc_wait: stat->fd=%d,last_fd=%d,last_count=%d\n",stat->fd,last_fd,last_count); +///**/ retval = ia64_ssc(arg0,0,0,0,ssc); +/**/ retval = 0; + } + else retval = -1L; + vcpu_set_gr(current,8,retval); + break; + case SSC_OPEN: + arg1 = vcpu_get_gr(current,33); // access rights +if (!running_on_sim) { printf("SSC_OPEN, not implemented on hardware. (ignoring...)\n"); arg0 = 0; } + if (arg0) { // metaphysical address + arg0 = translate_domain_mpaddr(arg0); + retval = ia64_ssc(arg0,arg1,0,0,ssc); + } + else retval = -1L; + vcpu_set_gr(current,8,retval); + break; + case SSC_WRITE: + case SSC_READ: +//if (ssc == SSC_WRITE) printf("DOING AN SSC_WRITE\n"); + arg1 = vcpu_get_gr(current,33); + arg2 = vcpu_get_gr(current,34); + arg3 = vcpu_get_gr(current,35); + if (arg2) { // metaphysical address of descriptor + struct ssc_disk_req *req; + unsigned long mpaddr, paddr; + long len; + + arg2 = translate_domain_mpaddr(arg2); + req = (struct disk_req *)__va(arg2); + req->len &= 0xffffffffL; // avoid strange bug + len = req->len; +/**/ last_fd = arg1; +/**/ last_count = len; + mpaddr = req->addr; +//if (last_count >= PAGE_SIZE) printf("do_ssc: read fd=%d, addr=%p, len=%lx ",last_fd,mpaddr,len); + retval = 0; + if ((mpaddr & PAGE_MASK) != ((mpaddr+len-1) & PAGE_MASK)) { + // do partial page first + req->addr = translate_domain_mpaddr(mpaddr); + req->len = PAGE_SIZE - (req->addr & ~PAGE_MASK); + len -= req->len; mpaddr += req->len; + retval = ia64_ssc(arg0,arg1,arg2,arg3,ssc); + arg3 += req->len; // file offset +/**/ last_stat.fd = last_fd; +/**/ (void)ia64_ssc(__pa(&last_stat),0,0,0,SSC_WAIT_COMPLETION); +//if (last_count >= PAGE_SIZE) printf("ssc(%p,%lx)[part]=%x ",req->addr,req->len,retval); + } + if (retval >= 0) while (len > 0) { + req->addr = translate_domain_mpaddr(mpaddr); + req->len = (len > PAGE_SIZE) ? PAGE_SIZE : len; + len -= PAGE_SIZE; mpaddr += PAGE_SIZE; + retval = ia64_ssc(arg0,arg1,arg2,arg3,ssc); + arg3 += req->len; // file offset +// TEMP REMOVED AGAIN arg3 += req->len; // file offset +/**/ last_stat.fd = last_fd; +/**/ (void)ia64_ssc(__pa(&last_stat),0,0,0,SSC_WAIT_COMPLETION); +//if (last_count >= PAGE_SIZE) printf("ssc(%p,%lx)=%x ",req->addr,req->len,retval); + } + // set it back to the original value + req->len = last_count; + } + else retval = -1L; + vcpu_set_gr(current,8,retval); +//if (last_count >= PAGE_SIZE) printf("retval=%x\n",retval); + break; + case SSC_CONNECT_INTERRUPT: + arg1 = vcpu_get_gr(current,33); + arg2 = vcpu_get_gr(current,34); + arg3 = vcpu_get_gr(current,35); + if (!running_on_sim) { printf("SSC_CONNECT_INTERRUPT, not implemented on hardware. (ignoring...)\n"); break; } + (void)ia64_ssc(arg0,arg1,arg2,arg3,ssc); + break; + case SSC_NETDEV_PROBE: + vcpu_set_gr(current,8,-1L); + break; + default: + printf("ia64_handle_break: bad ssc code %lx\n",ssc); + break; + } + vcpu_increment_iip(current); +} + +void fooefi(void) {} + +void +ia64_handle_break (unsigned long ifa, struct pt_regs *regs, unsigned long isr, unsigned long iim) +{ + static int first_time = 1; + struct domain *d = (struct domain *) current; + extern unsigned long running_on_sim; + + if (first_time) { + if (platform_is_hp_ski()) running_on_sim = 1; + else running_on_sim = 0; + first_time = 0; + } + if (iim == 0x80001 || iim == 0x80002) { //FIXME: don't hardcode constant + if (running_on_sim) do_ssc(vcpu_get_gr(current,36), regs); + else do_ssc(vcpu_get_gr(current,36), regs); + } + else if (iim == d->breakimm) { + struct ia64_sal_retval x; + switch (regs->r2) { + case FW_HYPERCALL_PAL_CALL: + //printf("*** PAL hypercall: index=%d\n",regs->r28); + //FIXME: This should call a C routine + x = pal_emulator_static(regs->r28); + regs->r8 = x.status; regs->r9 = x.v0; + regs->r10 = x.v1; regs->r11 = x.v2; + break; + case FW_HYPERCALL_SAL_CALL: + x = sal_emulator(vcpu_get_gr(d,32),vcpu_get_gr(d,33), + vcpu_get_gr(d,34),vcpu_get_gr(d,35), + vcpu_get_gr(d,36),vcpu_get_gr(d,37), + vcpu_get_gr(d,38),vcpu_get_gr(d,39)); + regs->r8 = x.status; regs->r9 = x.v0; + regs->r10 = x.v1; regs->r11 = x.v2; + break; + case FW_HYPERCALL_EFI_RESET_SYSTEM: + printf("efi.reset_system called "); + if (current == dom0) { + printf("(by dom0)\n "); + (*efi.reset_system)(EFI_RESET_WARM,0,0,NULL); + } + printf("(not supported for non-0 domain)\n"); + regs->r8 = EFI_UNSUPPORTED; + break; + case FW_HYPERCALL_EFI_GET_TIME: + { + unsigned long *tv, *tc; + fooefi(); + tv = vcpu_get_gr(d,32); + tc = vcpu_get_gr(d,33); + //printf("efi_get_time(%p,%p) called...",tv,tc); + tv = __va(translate_domain_mpaddr(tv)); + if (tc) tc = __va(translate_domain_mpaddr(tc)); + regs->r8 = (*efi.get_time)(tv,tc); + //printf("and returns %lx\n",regs->r8); + } + break; + case FW_HYPERCALL_EFI_SET_TIME: + case FW_HYPERCALL_EFI_GET_WAKEUP_TIME: + case FW_HYPERCALL_EFI_SET_WAKEUP_TIME: + // FIXME: need fixes in efi.h from 2.6.9 + case FW_HYPERCALL_EFI_SET_VIRTUAL_ADDRESS_MAP: + // FIXME: WARNING!! IF THIS EVER GETS IMPLEMENTED + // SOME OF THE OTHER EFI EMULATIONS WILL CHANGE AS + // POINTER ARGUMENTS WILL BE VIRTUAL!! + case FW_HYPERCALL_EFI_GET_VARIABLE: + // FIXME: need fixes in efi.h from 2.6.9 + case FW_HYPERCALL_EFI_GET_NEXT_VARIABLE: + case FW_HYPERCALL_EFI_SET_VARIABLE: + case FW_HYPERCALL_EFI_GET_NEXT_HIGH_MONO_COUNT: + // FIXME: need fixes in efi.h from 2.6.9 + regs->r8 = EFI_UNSUPPORTED; + break; + } + vcpu_increment_iip(current); + } + else reflect_interruption(ifa,isr,iim,regs,IA64_BREAK_VECTOR); +} + +void +ia64_handle_privop (unsigned long ifa, struct pt_regs *regs, unsigned long isr, unsigned long itir) +{ + IA64FAULT vector; + struct domain *d = (struct domain *) current; + // FIXME: no need to pass itir in to this routine as we need to + // compute the virtual itir anyway (based on domain's RR.ps) + // AND ACTUALLY reflect_interruption doesn't use it anyway! + itir = vcpu_get_itir_on_fault(d,ifa); + vector = priv_emulate((struct domain *)current,regs,isr); + if (vector == IA64_RETRY) { + reflect_interruption(ifa,isr,itir,regs, + IA64_ALT_DATA_TLB_VECTOR | IA64_FORCED_IFA); + } + else if (vector != IA64_NO_FAULT && vector != IA64_RFI_IN_PROGRESS) { + reflect_interruption(ifa,isr,itir,regs,vector); + } +} + +#define INTR_TYPE_MAX 10 +UINT64 int_counts[INTR_TYPE_MAX]; + +void +ia64_handle_reflection (unsigned long ifa, struct pt_regs *regs, unsigned long isr, unsigned long iim, unsigned long vector) +{ + extern unsigned long vcpu_get_itir_on_fault(struct domain *vcpu, UINT64 ifa); + struct domain *d = (struct domain *) current; + unsigned long check_lazy_cover = 0; + unsigned long psr = regs->cr_ipsr; + unsigned long itir = vcpu_get_itir_on_fault(d,ifa); + + if (!(psr & IA64_PSR_CPL)) { + printf("ia64_handle_reflection: reflecting with priv=0!!\n"); + while(1); + } + // FIXME: no need to pass itir in to this routine as we need to + // compute the virtual itir anyway (based on domain's RR.ps) + // AND ACTUALLY reflect_interruption doesn't use it anyway! + itir = vcpu_get_itir_on_fault(d,ifa); + switch(vector) { + case 8: + vector = IA64_DIRTY_BIT_VECTOR; break; + case 9: + vector = IA64_INST_ACCESS_BIT_VECTOR; break; + case 10: + check_lazy_cover = 1; + vector = IA64_DATA_ACCESS_BIT_VECTOR; break; + case 22: + vector = IA64_INST_ACCESS_RIGHTS_VECTOR; break; + case 23: + check_lazy_cover = 1; + vector = IA64_DATA_ACCESS_RIGHTS_VECTOR; break; + case 25: + vector = IA64_DISABLED_FPREG_VECTOR; break; + case 26: +printf("*** NaT fault... attempting to handle as privop\n"); + vector = priv_emulate(d,regs,isr); + if (vector == IA64_NO_FAULT) { +printf("*** Handled privop masquerading as NaT fault\n"); + return; + } + vector = IA64_NAT_CONSUMPTION_VECTOR; break; + case 27: +//printf("*** Handled speculation vector, itc=%lx!\n",ia64_get_itc()); + itir = iim; + vector = IA64_SPECULATION_VECTOR; break; + case 30: + // FIXME: Should we handle unaligned refs in Xen?? + vector = IA64_UNALIGNED_REF_VECTOR; break; + default: + printf("ia64_handle_reflection: unhandled vector=0x%lx\n",vector); + while(vector); + return; + } + if (check_lazy_cover && handle_lazy_cover(d, isr, regs)) return; + reflect_interruption(ifa,isr,itir,regs,vector); +} diff --git a/xen/arch/ia64/regionreg.c b/xen/arch/ia64/regionreg.c new file mode 100644 index 0000000000..bb1803a71d --- /dev/null +++ b/xen/arch/ia64/regionreg.c @@ -0,0 +1,399 @@ +/* + * Region register and region id management + * + * Copyright (C) 2001-2004 Hewlett-Packard Co. + * Dan Magenheimer (dan.magenheimer@hp.com + * Bret Mckee (bret.mckee@hp.com) + * + */ + + +#include +#include +#include +#include +#include +#include + + +#define IA64_MIN_IMPL_RID_BITS (IA64_MIN_IMPL_RID_MSB+1) +#define IA64_MAX_IMPL_RID_BITS 24 + +#define MIN_RIDS (1 << IA64_MIN_IMPL_RID_BITS) +#define MIN_RID_MAX (MIN_RIDS - 1) +#define MIN_RID_MASK (MIN_RIDS - 1) +#define MAX_RIDS (1 << (IA64_MAX_IMPL_RID_BITS)) +#define MAX_RID (MAX_RIDS - 1) +#define MAX_RID_BLOCKS (1 << (IA64_MAX_IMPL_RID_BITS-IA64_MIN_IMPL_RID_BITS)) +#define RIDS_PER_RIDBLOCK MIN_RIDS + +// This is the one global memory representation of the default Xen region reg +ia64_rr xen_rr; + +#if 0 +// following already defined in include/asm-ia64/gcc_intrin.h +// it should probably be ifdef'd out from there to ensure all region +// register usage is encapsulated in this file +static inline unsigned long +ia64_get_rr (unsigned long rr) +{ + unsigned long r; + __asm__ __volatile__ (";;mov %0=rr[%1];;":"=r"(r):"r"(rr):"memory"); + return r; +} + +static inline void +ia64_set_rr (unsigned long rr, unsigned long rrv) +{ + __asm__ __volatile__ (";;mov rr[%0]=%1;;"::"r"(rr),"r"(rrv):"memory"); +} +#endif + +// use this to allocate a rid out of the "Xen reserved rid block" +unsigned long allocate_reserved_rid(void) +{ + static unsigned long currentrid = XEN_DEFAULT_RID; + unsigned long t = currentrid; + + unsigned long max = RIDS_PER_RIDBLOCK; + + if (++currentrid >= max) return(-1UL); + return t; +} + + +// returns -1 if none available +unsigned long allocate_metaphysical_rid(void) +{ + unsigned long rid = allocate_reserved_rid(); +} + +int deallocate_metaphysical_rid(unsigned long rid) +{ + // fix this when the increment allocation mechanism is fixed. + return 1; +} + + +void init_rr(void) +{ + xen_rr.rrval = 0; + xen_rr.ve = 0; + xen_rr.rid = allocate_reserved_rid(); + xen_rr.ps = PAGE_SHIFT; + + printf("initialized xen_rr.rid=0x%lx\n", xen_rr.rid); +} + +/************************************* + Region Block setup/management +*************************************/ + +static int implemented_rid_bits = 0; +static struct domain *ridblock_owner[MAX_RID_BLOCKS] = { 0 }; + +void get_impl_rid_bits(void) +{ + // FIXME (call PAL) +//#ifdef CONFIG_MCKINLEY + implemented_rid_bits = IA64_MAX_IMPL_RID_BITS; +//#else +//#error "rid ranges won't work on Merced" +//#endif + if (implemented_rid_bits <= IA64_MIN_IMPL_RID_BITS || + implemented_rid_bits > IA64_MAX_IMPL_RID_BITS) + BUG(); +} + + +/* + * Allocate a power-of-two-sized chunk of region id space -- one or more + * "rid blocks" + */ +int allocate_rid_range(struct domain *d, unsigned long ridbits) +{ + int i, j, n_rid_blocks; + + if (implemented_rid_bits == 0) get_impl_rid_bits(); + + if (ridbits >= IA64_MAX_IMPL_RID_BITS) + ridbits = IA64_MAX_IMPL_RID_BITS - 1; + + if (ridbits < IA64_MIN_IMPL_RID_BITS) + ridbits = IA64_MIN_IMPL_RID_BITS; + + // convert to rid_blocks and find one + n_rid_blocks = ridbits - IA64_MIN_IMPL_RID_BITS + 1; + + // skip over block 0, reserved for "meta-physical mappings (and Xen)" + for (i = n_rid_blocks; i < MAX_RID_BLOCKS; i += n_rid_blocks) { + if (ridblock_owner[i] == NULL) { + for (j = i; j < i + n_rid_blocks; ++j) { + if (ridblock_owner[j]) break; + } + if (ridblock_owner[j] == NULL) break; + } + } + + if (i >= MAX_RID_BLOCKS) return 0; + + // found an unused block: + // (i << min_rid_bits) <= rid < ((i + n) << min_rid_bits) + // mark this block as owned + for (j = i; j < i + n_rid_blocks; ++j) ridblock_owner[j] = d; + + // setup domain struct + d->rid_bits = ridbits; + d->starting_rid = i << IA64_MIN_IMPL_RID_BITS; + d->ending_rid = (i+n_rid_blocks) << IA64_MIN_IMPL_RID_BITS; + + return 1; +} + + +int deallocate_rid_range(struct domain *d) +{ + int i; + int rid_block_end = d->ending_rid >> IA64_MIN_IMPL_RID_BITS; + int rid_block_start = d->starting_rid >> IA64_MIN_IMPL_RID_BITS; + + return 1; // KLUDGE ALERT + // + // not all domains will have allocated RIDs (physical mode loaders for instance) + // + if (d->rid_bits == 0) return 1; + +#ifdef DEBUG + for (i = rid_block_start; i < rid_block_end; ++i) { + ASSERT(ridblock_owner[i] == d); + } +#endif + + for (i = rid_block_start; i < rid_block_end; ++i) + ridblock_owner[i] = NULL; + + d->rid_bits = 0; + d->starting_rid = 0; + d->ending_rid = 0; + return 1; +} + + +// This function is purely for performance... apparently scrambling +// bits in the region id makes for better hashing, which means better +// use of the VHPT, which means better performance +// Note that the only time a RID should be mangled is when it is stored in +// a region register; anytime it is "viewable" outside of this module, +// it should be unmangled + +//This appears to work in Xen... turn it on later so no complications yet +//#define CONFIG_MANGLE_RIDS +#ifdef CONFIG_MANGLE_RIDS +static inline unsigned long +vmMangleRID(unsigned long RIDVal) +{ + union bits64 { unsigned char bytes[4]; unsigned long uint; }; + + union bits64 t; + unsigned char tmp; + + t.uint = RIDVal; + tmp = t.bytes[1]; + t.bytes[1] = t.bytes[3]; + t.bytes[3] = tmp; + + return t.uint; +} + +// since vmMangleRID is symmetric, use it for unmangling also +#define vmUnmangleRID(x) vmMangleRID(x) +#else +// no mangling/unmangling +#define vmMangleRID(x) (x) +#define vmUnmangleRID(x) (x) +#endif + +static inline void +set_rr_no_srlz(unsigned long rr, unsigned long rrval) +{ + ia64_set_rr(rr, vmMangleRID(rrval)); +} + +void +set_rr(unsigned long rr, unsigned long rrval) +{ + ia64_set_rr(rr, vmMangleRID(rrval)); + ia64_srlz_d(); +} + +unsigned long +get_rr(unsigned long rr) +{ + return vmUnmangleRID(ia64_get_rr(rr)); +} + +static inline int validate_page_size(unsigned long ps) +{ + switch(ps) { + case 12: case 13: case 14: case 16: case 18: + case 20: case 22: case 24: case 26: case 28: + return 1; + default: + return 0; + } +} + +// validates and changes a single region register +// in the currently executing domain +// Passing a value of -1 is a (successful) no-op +// NOTE: DOES NOT SET VCPU's rrs[x] value!! +int set_one_rr(unsigned long rr, unsigned long val) +{ + struct domain *d = current; + unsigned long rreg = REGION_NUMBER(rr); + ia64_rr rrv, newrrv, memrrv; + unsigned long newrid; + + if (val == -1) return 1; + + rrv.rrval = val; + newrrv.rrval = 0; + newrid = d->starting_rid + rrv.rid; + + if (newrid > d->ending_rid) return 0; + + memrrv.rrval = rrv.rrval; + if (rreg == 7) { + newrrv.rid = newrid; + newrrv.ve = VHPT_ENABLED_REGION_7; + newrrv.ps = IA64_GRANULE_SHIFT; + ia64_new_rr7(vmMangleRID(newrrv.rrval)); + } + else { + newrrv.rid = newrid; + // FIXME? region 6 needs to be uncached for EFI to work + if (rreg == 6) newrrv.ve = VHPT_ENABLED_REGION_7; + else newrrv.ve = VHPT_ENABLED_REGION_0_TO_6; + newrrv.ps = PAGE_SHIFT; + set_rr(rr,newrrv.rrval); + } + return 1; +} + +// set rr0 to the passed rid (for metaphysical mode so don't use domain offset +int set_metaphysical_rr(unsigned long rr, unsigned long rid) +{ + ia64_rr rrv; + + rrv.rrval = 0; + rrv.rid = rid; + rrv.ps = PAGE_SHIFT; +// rrv.ve = 1; FIXME: TURN ME BACK ON WHEN VHPT IS WORKING + rrv.ve = 0; + set_rr(rr,rrv.rrval); +} + +// validates/changes region registers 0-6 in the currently executing domain +// Note that this is the one and only SP API (other than executing a privop) +// for a domain to use to change region registers +int set_all_rr( u64 rr0, u64 rr1, u64 rr2, u64 rr3, + u64 rr4, u64 rr5, u64 rr6, u64 rr7) +{ + if (!set_one_rr(0x0000000000000000L, rr0)) return 0; + if (!set_one_rr(0x2000000000000000L, rr1)) return 0; + if (!set_one_rr(0x4000000000000000L, rr2)) return 0; + if (!set_one_rr(0x6000000000000000L, rr3)) return 0; + if (!set_one_rr(0x8000000000000000L, rr4)) return 0; + if (!set_one_rr(0xa000000000000000L, rr5)) return 0; + if (!set_one_rr(0xc000000000000000L, rr6)) return 0; + if (!set_one_rr(0xe000000000000000L, rr7)) return 0; + return 1; +} + +void init_all_rr(struct domain *d) +{ + ia64_rr rrv; + + rrv.rrval = 0; + rrv.rid = d->metaphysical_rid; + rrv.ps = PAGE_SHIFT; + rrv.ve = 1; + d->shared_info->arch.rrs[0] = -1; + d->shared_info->arch.rrs[1] = rrv.rrval; + d->shared_info->arch.rrs[2] = rrv.rrval; + d->shared_info->arch.rrs[3] = rrv.rrval; + d->shared_info->arch.rrs[4] = rrv.rrval; + d->shared_info->arch.rrs[5] = rrv.rrval; + d->shared_info->arch.rrs[6] = rrv.rrval; +// d->shared_info->arch.rrs[7] = rrv.rrval; +} + + +/* XEN/ia64 INTERNAL ROUTINES */ + +unsigned long physicalize_rid(struct domain *d, unsigned long rid) +{ + ia64_rr rrv; + + rrv.rrval = rid; + rrv.rid += d->starting_rid; + return rrv.rrval; +} + +unsigned long +virtualize_rid(struct domain *d, unsigned long rid) +{ + ia64_rr rrv; + + rrv.rrval = rid; + rrv.rid -= d->starting_rid; + return rrv.rrval; +} + +// loads a thread's region register (0-6) state into +// the real physical region registers. Returns the +// (possibly mangled) bits to store into rr7 +// iff it is different than what is currently in physical +// rr7 (because we have to to assembly and physical mode +// to change rr7). If no change to rr7 is required, returns 0. +// +unsigned long load_region_regs(struct domain *d) +{ + unsigned long rr0, rr1,rr2, rr3, rr4, rr5, rr6; + unsigned long oldrr7, newrr7; + // TODO: These probably should be validated + + if (d->metaphysical_mode) { + ia64_rr rrv; + + rrv.rid = d->metaphysical_rid; + rrv.ps = PAGE_SHIFT; + rrv.ve = 1; + rr0 = rr1 = rr2 = rr3 = rr4 = rr5 = rr6 = newrr7 = rrv.rrval; + } + else { + rr0 = physicalize_rid(d, d->shared_info->arch.rrs[0]); + rr1 = physicalize_rid(d, d->shared_info->arch.rrs[1]); + rr2 = physicalize_rid(d, d->shared_info->arch.rrs[2]); + rr3 = physicalize_rid(d, d->shared_info->arch.rrs[3]); + rr4 = physicalize_rid(d, d->shared_info->arch.rrs[4]); + rr5 = physicalize_rid(d, d->shared_info->arch.rrs[5]); + rr6 = physicalize_rid(d, d->shared_info->arch.rrs[6]); + newrr7 = physicalize_rid(d, d->shared_info->arch.rrs[7]); + } + + set_rr_no_srlz(0x0000000000000000L, rr0); + set_rr_no_srlz(0x2000000000000000L, rr1); + set_rr_no_srlz(0x4000000000000000L, rr2); + set_rr_no_srlz(0x6000000000000000L, rr3); + set_rr_no_srlz(0x8000000000000000L, rr4); + set_rr_no_srlz(0xa000000000000000L, rr5); + set_rr_no_srlz(0xc000000000000000L, rr6); + ia64_srlz_d(); + oldrr7 = get_rr(0xe000000000000000L); + if (oldrr7 != newrr7) { + newrr7 = (newrr7 & ~0xff) | (PAGE_SHIFT << 2) | 1; + return vmMangleRID(newrr7); + } + else return 0; +} diff --git a/xen/arch/ia64/vcpu.c b/xen/arch/ia64/vcpu.c new file mode 100644 index 0000000000..d0d62771e8 --- /dev/null +++ b/xen/arch/ia64/vcpu.c @@ -0,0 +1,1559 @@ +/* + * Virtualized CPU functions + * + * Copyright (C) 2004 Hewlett-Packard Co. + * Dan Magenheimer (dan.magenheimer@hp.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef union { + struct ia64_psr; + unsigned long i64; +} PSR; + +//typedef struct pt_regs REGS; +//typedef struct domain VCPU; + +// this def for vcpu_regs won't work if kernel stack is present +#define vcpu_regs(vcpu) ((struct pt_regs *) vcpu->regs) +#define PSCB(x) x->shared_info->arch + +#define TRUE 1 +#define FALSE 0 +#define IA64_PTA_SZ_BIT 2 +#define IA64_PTA_VF_BIT 8 +#define IA64_PTA_BASE_BIT 15 +#define IA64_PTA_LFMT (1UL << IA64_PTA_VF_BIT) +#define IA64_PTA_SZ(x) (x##UL << IA64_PTA_SZ_BIT) + +#define STATIC + +unsigned long vcpu_verbose = 0; +#define verbose(a...) do {if (vcpu_verbose) printf(a);} while(0) + +/************************************************************************** + VCPU general register access routines +**************************************************************************/ + +UINT64 +vcpu_get_gr(VCPU *vcpu, unsigned reg) +{ + REGS *regs = vcpu_regs(vcpu); + UINT64 val; + + if (!reg) return 0; + getreg(reg,&val,0,regs); // FIXME: handle NATs later + return val; +} + +// returns: +// IA64_ILLOP_FAULT if the register would cause an Illegal Operation fault +// IA64_NO_FAULT otherwise +IA64FAULT +vcpu_set_gr(VCPU *vcpu, unsigned reg, UINT64 value) +{ + REGS *regs = vcpu_regs(vcpu); + long sof = (regs->cr_ifs) & 0x7f; + + if (!reg) return IA64_ILLOP_FAULT; + if (reg >= sof + 32) return IA64_ILLOP_FAULT; + setreg(reg,value,0,regs); // FIXME: handle NATs later + return IA64_NO_FAULT; +} + +/************************************************************************** + VCPU privileged application register access routines +**************************************************************************/ + +IA64FAULT vcpu_set_ar(VCPU *vcpu, UINT64 reg, UINT64 val) +{ + if (reg == 44) return (vcpu_set_itc(vcpu,val)); + if (reg == 27) return (IA64_ILLOP_FAULT); + if (reg > 7) return (IA64_ILLOP_FAULT); + PSCB(vcpu).krs[reg] = val; +#if 0 +// for now, privify kr read's so all kr accesses are privileged + switch (reg) { + case 0: asm volatile ("mov ar.k0=%0" :: "r"(val)); break; + case 1: asm volatile ("mov ar.k1=%0" :: "r"(val)); break; + case 2: asm volatile ("mov ar.k2=%0" :: "r"(val)); break; + case 3: asm volatile ("mov ar.k3=%0" :: "r"(val)); break; + case 4: asm volatile ("mov ar.k4=%0" :: "r"(val)); break; + case 5: asm volatile ("mov ar.k5=%0" :: "r"(val)); break; + case 6: asm volatile ("mov ar.k6=%0" :: "r"(val)); break; + case 7: asm volatile ("mov ar.k7=%0" :: "r"(val)); break; + case 27: asm volatile ("mov ar.cflg=%0" :: "r"(val)); break; + } +#endif + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_get_ar(VCPU *vcpu, UINT64 reg, UINT64 *val) +{ + if (reg > 7) return (IA64_ILLOP_FAULT); + *val = PSCB(vcpu).krs[reg]; + return IA64_NO_FAULT; +} + +/************************************************************************** + VCPU processor status register access routines +**************************************************************************/ + +void vcpu_set_metaphysical_mode(VCPU *vcpu, BOOLEAN newmode) +{ + /* only do something if mode changes */ + if (!!newmode ^ !!vcpu->metaphysical_mode) { + if (newmode) set_metaphysical_rr(0,vcpu->metaphysical_rid); + else if (PSCB(vcpu).rrs[0] != -1) + set_one_rr(0, PSCB(vcpu).rrs[0]); + vcpu->metaphysical_mode = newmode; + } +} + +IA64FAULT vcpu_reset_psr_sm(VCPU *vcpu, UINT64 imm24) +{ + struct ia64_psr psr, imm, *ipsr; + REGS *regs = vcpu_regs(vcpu); + + // TODO: All of these bits need to be virtualized + // TODO: Only allowed for current vcpu + __asm__ __volatile ("mov %0=psr;;" : "=r"(psr) :: "memory"); + ipsr = (struct ia64_psr *)®s->cr_ipsr; + imm = *(struct ia64_psr *)&imm24; + // interrupt flag + if (imm.i) PSCB(vcpu).interrupt_delivery_enabled = 0; + if (imm.ic) PSCB(vcpu).interrupt_collection_enabled = 0; + // interrupt collection flag + //if (imm.ic) PSCB(vcpu).interrupt_delivery_enabled = 0; + // just handle psr.up and psr.pp for now + if (imm24 & ~(IA64_PSR_PP | IA64_PSR_UP | IA64_PSR_SP + | IA64_PSR_I | IA64_PSR_IC | IA64_PSR_DT + | IA64_PSR_DFL | IA64_PSR_DFH)) + return (IA64_ILLOP_FAULT); + if (imm.dfh) ipsr->dfh = 0; + if (imm.dfl) ipsr->dfl = 0; + if (imm.pp) { ipsr->pp = 0; psr.pp = 0; } + if (imm.up) { ipsr->up = 0; psr.up = 0; } + if (imm.sp) { ipsr->sp = 0; psr.sp = 0; } + if (imm.dt) vcpu_set_metaphysical_mode(vcpu,TRUE); + __asm__ __volatile (";; mov psr.l=%0;; srlz.d"::"r"(psr):"memory"); + return IA64_NO_FAULT; +} + +extern UINT64 vcpu_check_pending_interrupts(VCPU *vcpu); +#define SPURIOUS_VECTOR 0xf + +IA64FAULT vcpu_set_psr_sm(VCPU *vcpu, UINT64 imm24) +{ + struct ia64_psr psr, imm, *ipsr; + REGS *regs = vcpu_regs(vcpu); + UINT64 mask, enabling_interrupts = 0; + + // TODO: All of these bits need to be virtualized + __asm__ __volatile ("mov %0=psr;;" : "=r"(psr) :: "memory"); + imm = *(struct ia64_psr *)&imm24; + ipsr = (struct ia64_psr *)®s->cr_ipsr; + // just handle psr.sp,pp and psr.i,ic (and user mask) for now + mask = IA64_PSR_PP|IA64_PSR_SP|IA64_PSR_I|IA64_PSR_IC|IA64_PSR_UM | + IA64_PSR_DT|IA64_PSR_DFL|IA64_PSR_DFH; + if (imm24 & ~mask) return (IA64_ILLOP_FAULT); + if (imm.dfh) ipsr->dfh = 1; + if (imm.dfl) ipsr->dfl = 1; + if (imm.pp) { ipsr->pp = 1; psr.pp = 1; } + if (imm.sp) { ipsr->sp = 1; psr.sp = 1; } + if (imm.i) { + if (!PSCB(vcpu).interrupt_delivery_enabled) { +//printf("vcpu_set_psr_sm: psr.ic 0->1 "); + enabling_interrupts = 1; + } + PSCB(vcpu).interrupt_delivery_enabled = 1; + } + if (imm.ic) PSCB(vcpu).interrupt_collection_enabled = 1; + // TODO: do this faster + if (imm.mfl) { ipsr->mfl = 1; psr.mfl = 1; } + if (imm.ac) { ipsr->ac = 1; psr.ac = 1; } + if (imm.up) { ipsr->up = 1; psr.up = 1; } + if (imm.be) { + printf("*** DOMAIN TRYING TO TURN ON BIG-ENDIAN!!!\n"); + return (IA64_ILLOP_FAULT); + } + if (imm.dt) vcpu_set_metaphysical_mode(vcpu,FALSE); + __asm__ __volatile (";; mov psr.l=%0;; srlz.d"::"r"(psr):"memory"); +#if 0 // now done with deliver_pending_interrupts + if (enabling_interrupts) { + if (vcpu_check_pending_interrupts(vcpu) != SPURIOUS_VECTOR) { +//printf("with interrupts pending\n"); + return IA64_EXTINT_VECTOR; + } +//else printf("but nothing pending\n"); + } +#endif + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_psr_l(VCPU *vcpu, UINT64 val) +{ + struct ia64_psr psr, newpsr, *ipsr; + REGS *regs = vcpu_regs(vcpu); + UINT64 enabling_interrupts = 0; + + // TODO: All of these bits need to be virtualized + __asm__ __volatile ("mov %0=psr;;" : "=r"(psr) :: "memory"); + newpsr = *(struct ia64_psr *)&val; + ipsr = (struct ia64_psr *)®s->cr_ipsr; + // just handle psr.up and psr.pp for now + //if (val & ~(IA64_PSR_PP | IA64_PSR_UP | IA64_PSR_SP)) return (IA64_ILLOP_FAULT); + // however trying to set other bits can't be an error as it is in ssm + if (newpsr.dfh) ipsr->dfh = 1; + if (newpsr.dfl) ipsr->dfl = 1; + if (newpsr.pp) { ipsr->pp = 1; psr.pp = 1; } + if (newpsr.up) { ipsr->up = 1; psr.up = 1; } + if (newpsr.sp) { ipsr->sp = 1; psr.sp = 1; } + if (newpsr.i) { + if (!PSCB(vcpu).interrupt_delivery_enabled) + enabling_interrupts = 1; + PSCB(vcpu).interrupt_delivery_enabled = 1; + } + if (newpsr.ic) PSCB(vcpu).interrupt_collection_enabled = 1; + if (newpsr.mfl) { ipsr->mfl = 1; psr.mfl = 1; } + if (newpsr.ac) { ipsr->ac = 1; psr.ac = 1; } + if (newpsr.up) { ipsr->up = 1; psr.up = 1; } + if (newpsr.dt && newpsr.rt) vcpu_set_metaphysical_mode(vcpu,FALSE); + else vcpu_set_metaphysical_mode(vcpu,TRUE); + if (newpsr.be) { + printf("*** DOMAIN TRYING TO TURN ON BIG-ENDIAN!!!\n"); + return (IA64_ILLOP_FAULT); + } + //__asm__ __volatile (";; mov psr.l=%0;; srlz.d"::"r"(psr):"memory"); +#if 0 // now done with deliver_pending_interrupts + if (enabling_interrupts) { + if (vcpu_check_pending_interrupts(vcpu) != SPURIOUS_VECTOR) + return IA64_EXTINT_VECTOR; + } +#endif + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_get_psr(VCPU *vcpu, UINT64 *pval) +{ + UINT64 psr; + struct ia64_psr newpsr; + + // TODO: This needs to return a "filtered" view of + // the psr, not the actual psr. Probably the psr needs + // to be a field in regs (in addition to ipsr). + __asm__ __volatile ("mov %0=psr;;" : "=r"(psr) :: "memory"); + newpsr = *(struct ia64_psr *)&psr; + if (newpsr.cpl == 2) newpsr.cpl = 0; + if (PSCB(vcpu).interrupt_delivery_enabled) newpsr.i = 1; + else newpsr.i = 0; + if (PSCB(vcpu).interrupt_collection_enabled) newpsr.ic = 1; + else newpsr.ic = 0; + *pval = *(unsigned long *)&newpsr; + return IA64_NO_FAULT; +} + +BOOLEAN vcpu_get_psr_ic(VCPU *vcpu) +{ + return !!PSCB(vcpu).interrupt_collection_enabled; +} + +BOOLEAN vcpu_get_psr_i(VCPU *vcpu) +{ + return !!PSCB(vcpu).interrupt_delivery_enabled; +} + +UINT64 vcpu_get_ipsr_int_state(VCPU *vcpu,UINT64 prevpsr) +{ + UINT64 dcr = PSCB(vcpu).dcr; + PSR psr = {0}; + + //printf("*** vcpu_get_ipsr_int_state (0x%016lx)...",prevpsr); + psr.i64 = prevpsr; + psr.be = 0; if (dcr & IA64_DCR_BE) psr.be = 1; + psr.pp = 0; if (dcr & IA64_DCR_PP) psr.pp = 1; + psr.ic = PSCB(vcpu).interrupt_collection_enabled; + psr.i = PSCB(vcpu).interrupt_delivery_enabled; + psr.bn = PSCB(vcpu).banknum; + psr.dt = 1; psr.it = 1; psr.rt = 1; + if (psr.cpl == 2) psr.cpl = 0; // !!!! fool domain + // psr.pk = 1; + //printf("returns 0x%016lx...",psr.i64); + return psr.i64; +} + +/************************************************************************** + VCPU control register access routines +**************************************************************************/ + +IA64FAULT vcpu_get_dcr(VCPU *vcpu, UINT64 *pval) +{ +extern unsigned long privop_trace; +//privop_trace=0; +//verbose("vcpu_get_dcr: called @%p\n",PSCB(vcpu).iip); + // Reads of cr.dcr on Xen always have the sign bit set, so + // a domain can differentiate whether it is running on SP or not + *pval = PSCB(vcpu).dcr | 0x8000000000000000L; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_iva(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).iva & ~0x7fffL; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_pta(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).pta; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_ipsr(VCPU *vcpu, UINT64 *pval) +{ + //REGS *regs = vcpu_regs(vcpu); + //*pval = regs->cr_ipsr; + *pval = PSCB(vcpu).ipsr; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_isr(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).isr; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_iip(VCPU *vcpu, UINT64 *pval) +{ + //REGS *regs = vcpu_regs(vcpu); + //*pval = regs->cr_iip; + *pval = PSCB(vcpu).iip; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_ifa(VCPU *vcpu, UINT64 *pval) +{ + UINT64 val = PSCB(vcpu).ifa; + *pval = val; + return (IA64_NO_FAULT); +} + + +unsigned long vcpu_get_itir_on_fault(VCPU *vcpu, UINT64 ifa) +{ + ia64_rr rr; + + rr.rrval = 0; + rr.ps = vcpu_get_rr_ps(vcpu,ifa); + rr.rid = vcpu_get_rr_rid(vcpu,ifa); + return (rr.rrval); +} + + +IA64FAULT vcpu_get_itir(VCPU *vcpu, UINT64 *pval) +{ + UINT64 val = PSCB(vcpu).itir; + *pval = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_iipa(VCPU *vcpu, UINT64 *pval) +{ + UINT64 val = PSCB(vcpu).iipa; + // SP entry code does not save iipa yet nor does it get + // properly delivered in the pscb + printf("*** vcpu_get_iipa: cr.iipa not fully implemented yet!!\n"); + *pval = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_ifs(VCPU *vcpu, UINT64 *pval) +{ + //PSCB(vcpu).ifs = PSCB(vcpu)->regs.cr_ifs; + //*pval = PSCB(vcpu).regs.cr_ifs; + *pval = PSCB(vcpu).ifs; + PSCB(vcpu).incomplete_regframe = 0; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_iim(VCPU *vcpu, UINT64 *pval) +{ + UINT64 val = PSCB(vcpu).iim; + *pval = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_iha(VCPU *vcpu, UINT64 *pval) +{ + return vcpu_thash(vcpu,PSCB(vcpu).ifa,pval); +} + +IA64FAULT vcpu_set_dcr(VCPU *vcpu, UINT64 val) +{ +extern unsigned long privop_trace; +//privop_trace=1; + // Reads of cr.dcr on SP always have the sign bit set, so + // a domain can differentiate whether it is running on SP or not + // Thus, writes of DCR should ignore the sign bit +//verbose("vcpu_set_dcr: called\n"); + PSCB(vcpu).dcr = val & ~0x8000000000000000L; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_iva(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).iva = val & ~0x7fffL; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_pta(VCPU *vcpu, UINT64 val) +{ + if (val & IA64_PTA_LFMT) { + printf("*** No support for VHPT long format yet!!\n"); + return (IA64_ILLOP_FAULT); + } + if (val & (0x3f<<9)) /* reserved fields */ return IA64_RSVDREG_FAULT; + if (val & 2) /* reserved fields */ return IA64_RSVDREG_FAULT; + PSCB(vcpu).pta = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_ipsr(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).ipsr = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_isr(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).isr = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_iip(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).iip = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_increment_iip(VCPU *vcpu) +{ + REGS *regs = vcpu_regs(vcpu); + struct ia64_psr *ipsr = (struct ia64_psr *)®s->cr_ipsr; + if (ipsr->ri == 2) { ipsr->ri=0; regs->cr_iip += 16; } + else ipsr->ri++; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_ifa(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).ifa = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_itir(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).itir = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_iipa(VCPU *vcpu, UINT64 val) +{ + // SP entry code does not save iipa yet nor does it get + // properly delivered in the pscb + printf("*** vcpu_set_iipa: cr.iipa not fully implemented yet!!\n"); + PSCB(vcpu).iipa = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_ifs(VCPU *vcpu, UINT64 val) +{ + //REGS *regs = vcpu_regs(vcpu); + PSCB(vcpu).ifs = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_iim(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).iim = val; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_set_iha(VCPU *vcpu, UINT64 val) +{ + PSCB(vcpu).iha = val; + return IA64_NO_FAULT; +} + +/************************************************************************** + VCPU interrupt control register access routines +**************************************************************************/ + +void vcpu_pend_interrupt(VCPU *vcpu, UINT64 vector) +{ + if (vector & ~0xff) { + printf("vcpu_pend_interrupt: bad vector\n"); + return; + } + if (!test_bit(vector,PSCB(vcpu).delivery_mask)) return; + if (test_bit(vector,PSCB(vcpu).irr)) { +//printf("vcpu_pend_interrupt: overrun\n"); + } + set_bit(vector,PSCB(vcpu).irr); +} + +#define IA64_TPR_MMI 0x10000 +#define IA64_TPR_MIC 0x000f0 + +/* checks to see if a VCPU has any unmasked pending interrupts + * if so, returns the highest, else returns SPURIOUS_VECTOR */ +/* NOTE: Since this gets called from vcpu_get_ivr() and the + * semantics of "mov rx=cr.ivr" ignore the setting of the psr.i bit, + * this routine also ignores pscb.interrupt_delivery_enabled + * and this must be checked independently; see vcpu_deliverable interrupts() */ +UINT64 vcpu_check_pending_interrupts(VCPU *vcpu) +{ + UINT64 *p, *q, *r, bits, bitnum, mask, i, vector; + + p = &PSCB(vcpu).irr[3]; + q = &PSCB(vcpu).delivery_mask[3]; + r = &PSCB(vcpu).insvc[3]; + for (i = 3; ; p--, q--, r--, i--) { + bits = *p & *q; + if (bits) break; // got a potential interrupt + if (*r) { + // nothing in this word which is pending+inservice + // but there is one inservice which masks lower + return SPURIOUS_VECTOR; + } + if (i == 0) { + // checked all bits... nothing pending+inservice + return SPURIOUS_VECTOR; + } + } + // have a pending,deliverable interrupt... see if it is masked + bitnum = ia64_fls(bits); +//printf("XXXXXXX vcpu_check_pending_interrupts: got bitnum=%p...",bitnum); + vector = bitnum+(i*64); + mask = 1L << bitnum; +//printf("XXXXXXX vcpu_check_pending_interrupts: got vector=%p...",vector); + if (*r >= mask) { + // masked by equal inservice +//printf("but masked by equal inservice\n"); + return SPURIOUS_VECTOR; + } + if (PSCB(vcpu).tpr & IA64_TPR_MMI) { + // tpr.mmi is set +//printf("but masked by tpr.mmi\n"); + return SPURIOUS_VECTOR; + } + if (((PSCB(vcpu).tpr & IA64_TPR_MIC) + 15) >= vector) { + //tpr.mic masks class +//printf("but masked by tpr.mic\n"); + return SPURIOUS_VECTOR; + } + +//printf("returned to caller\n"); + return vector; +} + +UINT64 vcpu_deliverable_interrupts(VCPU *vcpu) +{ + return (vcpu_get_psr_i(vcpu) && + vcpu_check_pending_interrupts(vcpu) != SPURIOUS_VECTOR); +} + +IA64FAULT vcpu_get_lid(VCPU *vcpu, UINT64 *pval) +{ +extern unsigned long privop_trace; +//privop_trace=1; + //TODO: Implement this + printf("vcpu_get_lid: WARNING: Getting cr.lid always returns zero\n"); + *pval = 0; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_get_ivr(VCPU *vcpu, UINT64 *pval) +{ + int i; + UINT64 vector, mask; +#if 1 + static char firstivr = 1; + static char firsttime[256]; + if (firstivr) { + int i; + for (i=0;i<256;i++) firsttime[i]=1; + firstivr=0; + } +#endif + + vector = vcpu_check_pending_interrupts(vcpu); + if (vector == SPURIOUS_VECTOR) { + PSCB(vcpu).pending_interruption = 0; + *pval = vector; + return IA64_NO_FAULT; + } + // now have an unmasked, pending, deliverable vector! + // getting ivr has "side effects" +#if 0 + if (firsttime[vector]) { + printf("*** First get_ivr on vector=%d,itc=%lx\n", + vector,ia64_get_itc()); + firsttime[vector]=0; + } +#endif + i = vector >> 6; + mask = 1L << (vector & 0x3f); +//printf("ZZZZZZ vcpu_get_ivr: setting insvc mask for vector %ld\n",vector); + PSCB(vcpu).insvc[i] |= mask; + PSCB(vcpu).irr[i] &= ~mask; + PSCB(vcpu).pending_interruption--; + *pval = vector; + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_get_tpr(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).tpr; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_eoi(VCPU *vcpu, UINT64 *pval) +{ + *pval = 0L; // reads of eoi always return 0 + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_irr0(VCPU *vcpu, UINT64 *pval) +{ +#ifndef IRR_USE_FIXED + printk("vcpu_get_irr: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +#else + *pval = vcpu->irr[0]; + return (IA64_NO_FAULT); +#endif +} + +IA64FAULT vcpu_get_irr1(VCPU *vcpu, UINT64 *pval) +{ +#ifndef IRR_USE_FIXED + printk("vcpu_get_irr: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +#else + *pval = vcpu->irr[1]; + return (IA64_NO_FAULT); +#endif +} + +IA64FAULT vcpu_get_irr2(VCPU *vcpu, UINT64 *pval) +{ +#ifndef IRR_USE_FIXED + printk("vcpu_get_irr: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +#else + *pval = vcpu->irr[2]; + return (IA64_NO_FAULT); +#endif +} + +IA64FAULT vcpu_get_irr3(VCPU *vcpu, UINT64 *pval) +{ +#ifndef IRR_USE_FIXED + printk("vcpu_get_irr: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +#else + *pval = vcpu->irr[3]; + return (IA64_NO_FAULT); +#endif +} + +IA64FAULT vcpu_get_itv(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).itv; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_pmv(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).pmv; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_cmcv(VCPU *vcpu, UINT64 *pval) +{ + *pval = PSCB(vcpu).cmcv; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_lrr0(VCPU *vcpu, UINT64 *pval) +{ + // fix this when setting values other than m-bit is supported + printf("vcpu_get_lrr0: Unmasked interrupts unsupported\n"); + *pval = (1L << 16); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_lrr1(VCPU *vcpu, UINT64 *pval) +{ + // fix this when setting values other than m-bit is supported + printf("vcpu_get_lrr1: Unmasked interrupts unsupported\n"); + *pval = (1L << 16); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_lid(VCPU *vcpu, UINT64 val) +{ + printf("vcpu_set_lid: Setting cr.lid is unsupported\n"); + return (IA64_ILLOP_FAULT); +} + +IA64FAULT vcpu_set_tpr(VCPU *vcpu, UINT64 val) +{ + if (val & 0xff00) return IA64_RSVDREG_FAULT; + PSCB(vcpu).tpr = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_eoi(VCPU *vcpu, UINT64 val) +{ + UINT64 *p, bits, vec, bitnum; + int i; + + p = &PSCB(vcpu).insvc[3]; + for (i = 3; (i >= 0) && !(bits = *p); i--, p--); + if (i < 0) { + printf("Trying to EOI interrupt when none are in-service.\r\n"); + return; + } + bitnum = ia64_fls(bits); + vec = bitnum + (i*64); + /* clear the correct bit */ + bits &= ~(1L << bitnum); + *p = bits; + /* clearing an eoi bit may unmask another pending interrupt... */ + if (PSCB(vcpu).interrupt_delivery_enabled) { // but only if enabled... + // worry about this later... Linux only calls eoi + // with interrupts disabled + printf("Trying to EOI interrupt with interrupts enabled\r\n"); + } +//printf("YYYYY vcpu_set_eoi: Successful\n"); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_lrr0(VCPU *vcpu, UINT64 val) +{ + if (!(val & (1L << 16))) { + printf("vcpu_set_lrr0: Unmasked interrupts unsupported\n"); + return (IA64_ILLOP_FAULT); + } + // no place to save this state but nothing to do anyway + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_lrr1(VCPU *vcpu, UINT64 val) +{ + if (!(val & (1L << 16))) { + printf("vcpu_set_lrr0: Unmasked interrupts unsupported\n"); + return (IA64_ILLOP_FAULT); + } + // no place to save this state but nothing to do anyway + return (IA64_NO_FAULT); +} + + +IA64FAULT vcpu_set_itv(VCPU *vcpu, UINT64 val) +{ +extern unsigned long privop_trace; +//privop_trace=1; + if (val & 0xef00) return (IA64_ILLOP_FAULT); + PSCB(vcpu).itv = val; + if (val & 0x10000) { +printf("**** vcpu_set_itv(%d): vitm=%lx, setting to 0\n",val,PSCB(vcpu).domain_itm); + PSCB(vcpu).domain_itm = 0; + } + else vcpu_enable_timer(vcpu,1000000L); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_pmv(VCPU *vcpu, UINT64 val) +{ + if (val & 0xef00) /* reserved fields */ return IA64_RSVDREG_FAULT; + PSCB(vcpu).pmv = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_cmcv(VCPU *vcpu, UINT64 val) +{ + if (val & 0xef00) /* reserved fields */ return IA64_RSVDREG_FAULT; + PSCB(vcpu).cmcv = val; + return (IA64_NO_FAULT); +} + +/************************************************************************** +Interval timer routines +**************************************************************************/ + +BOOLEAN vcpu_timer_disabled(VCPU *vcpu) +{ + UINT64 itv = PSCB(vcpu).itv; + return(!itv || !!(itv & 0x10000)); +} + +BOOLEAN vcpu_timer_expired(VCPU *vcpu) +{ + unsigned long domain_itm = PSCB(vcpu).domain_itm; + unsigned long now = ia64_get_itc(); + + if (domain_itm && (now > domain_itm) && + !vcpu_timer_disabled(vcpu)) return TRUE; + return FALSE; +} + +void vcpu_safe_set_itm(unsigned long val) +{ + unsigned long epsilon = 100; + UINT64 now = ia64_get_itc(); + + local_irq_disable(); + while (1) { +//printf("*** vcpu_safe_set_itm: Setting itm to %lx, itc=%lx\n",val,now); + ia64_set_itm(val); + if (val > (now = ia64_get_itc())) break; + val = now + epsilon; + epsilon <<= 1; + } + local_irq_enable(); +} + +void vcpu_set_next_timer(VCPU *vcpu) +{ + UINT64 d = PSCB(vcpu).domain_itm; + //UINT64 s = PSCB(vcpu).xen_itm; + UINT64 s = local_cpu_data->itm_next; + UINT64 now = ia64_get_itc(); + //UINT64 interval = PSCB(vcpu).xen_timer_interval; + + /* gloss over the wraparound problem for now... we know it exists + * but it doesn't matter right now */ + +#if 0 + /* ensure at least next SP tick is in the future */ + if (!interval) PSCB(vcpu).xen_itm = now + +#if 0 + (running_on_sim() ? SIM_DEFAULT_CLOCK_RATE : + DEFAULT_CLOCK_RATE); +#else + 3000000; +//printf("vcpu_set_next_timer: HACK!\n"); +#endif +#if 0 + if (PSCB(vcpu).xen_itm < now) + while (PSCB(vcpu).xen_itm < now + (interval>>1)) + PSCB(vcpu).xen_itm += interval; +#endif +#endif + + if (is_idle_task(vcpu)) { + printf("****** vcpu_set_next_timer called during idle!!\n"); + } + //s = PSCB(vcpu).xen_itm; + if (d && (d > now) && (d < s)) { + vcpu_safe_set_itm(d); + //using_domain_as_itm++; + } + else { + vcpu_safe_set_itm(s); + //using_xen_as_itm++; + } +} + +// parameter is a time interval specified in cycles +void vcpu_enable_timer(VCPU *vcpu,UINT64 cycles) +{ + PSCB(vcpu).xen_timer_interval = cycles; + vcpu_set_next_timer(vcpu); + printf("vcpu_enable_timer(%d): interval set to %d cycles\n", + PSCB(vcpu).xen_timer_interval); + __set_bit(PSCB(vcpu).itv, PSCB(vcpu).delivery_mask); +} + +IA64FAULT vcpu_set_itm(VCPU *vcpu, UINT64 val) +{ + UINT now = ia64_get_itc(); + + //if (val < now) val = now + 1000; +//printf("*** vcpu_set_itm: called with %lx\n",val); + PSCB(vcpu).domain_itm = val; + vcpu_set_next_timer(vcpu); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_itc(VCPU *vcpu, UINT64 val) +{ + + UINT64 oldnow = ia64_get_itc(); + UINT64 olditm = PSCB(vcpu).domain_itm; + unsigned long d = olditm - oldnow; + unsigned long x = local_cpu_data->itm_next - oldnow; + + UINT64 newnow = val, min_delta; + + local_irq_disable(); + if (olditm) { +printf("**** vcpu_set_itc(%lx): vitm changed to %lx\n",val,newnow+d); + PSCB(vcpu).domain_itm = newnow + d; + } + local_cpu_data->itm_next = newnow + x; + d = PSCB(vcpu).domain_itm; + x = local_cpu_data->itm_next; + + ia64_set_itc(newnow); + if (d && (d > newnow) && (d < x)) { + vcpu_safe_set_itm(d); + //using_domain_as_itm++; + } + else { + vcpu_safe_set_itm(x); + //using_xen_as_itm++; + } + local_irq_enable(); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_itm(VCPU *vcpu, UINT64 *pval) +{ + //FIXME: Implement this + printf("vcpu_get_itm: Getting cr.itm is unsupported... continuing\n"); + return (IA64_NO_FAULT); + //return (IA64_ILLOP_FAULT); +} + +IA64FAULT vcpu_get_itc(VCPU *vcpu, UINT64 *pval) +{ + //TODO: Implement this + printf("vcpu_get_itc: Getting ar.itc is unsupported\n"); + return (IA64_ILLOP_FAULT); +} + +void vcpu_pend_timer(VCPU *vcpu) +{ + UINT64 itv = PSCB(vcpu).itv & 0xff; + + if (vcpu_timer_disabled(vcpu)) return; + vcpu_pend_interrupt(vcpu, itv); +} + +//FIXME: This is a hack because everything dies if a timer tick is lost +void vcpu_poke_timer(VCPU *vcpu) +{ + UINT64 itv = PSCB(vcpu).itv & 0xff; + UINT64 now = ia64_get_itc(); + UINT64 itm = PSCB(vcpu).domain_itm; + UINT64 irr; + + if (vcpu_timer_disabled(vcpu)) return; + if (!itm) return; + if (itv != 0xefL) { + printf("vcpu_poke_timer: unimplemented itv=%lx!\n",itv); + while(1); + } + // using 0xef instead of itv so can get real irr + if (now > itm && !test_bit(0xefL, PSCB(vcpu).insvc)) { + if (!test_bit(0xefL,PSCB(vcpu).irr)) { + irr = ia64_getreg(_IA64_REG_CR_IRR3); + if (irr & (1L<<(0xef-0xc0))) return; +if (now-itm>0x800000) +printf("*** poking timer: now=%lx,vitm=%lx,xitm=%lx,itm=%lx\n",now,itm,local_cpu_data->itm_next,ia64_get_itm()); + vcpu_pend_interrupt(vcpu, 0xefL); + } + } +} + + +/************************************************************************** +Privileged operation emulation routines +**************************************************************************/ + +IA64FAULT vcpu_force_data_miss(VCPU *vcpu, UINT64 ifa) +{ + PSCB(vcpu).ifa = ifa; // privop traps don't set ifa so do it here + return (IA64_DATA_TLB_VECTOR | IA64_FORCED_IFA); +} + + +IA64FAULT vcpu_rfi(VCPU *vcpu) +{ + // TODO: Only allowed for current vcpu + PSR psr; + UINT64 int_enable, regspsr = 0; + UINT64 ifs; + REGS *regs = vcpu_regs(vcpu); + extern void dorfirfi(void); + + psr.i64 = PSCB(vcpu).ipsr; + if (psr.cpl < 3) psr.cpl = 2; + if (psr.i) PSCB(vcpu).interrupt_delivery_enabled = 1; + int_enable = psr.i; + if (psr.ic) PSCB(vcpu).interrupt_collection_enabled = 1; + if (psr.dt && psr.rt && psr.it) vcpu_set_metaphysical_mode(vcpu,FALSE); + else vcpu_set_metaphysical_mode(vcpu,TRUE); + psr.ic = 1; psr.i = 1; + psr.dt = 1; psr.rt = 1; psr.it = 1; + psr.bn = 1; + //psr.pk = 1; // checking pkeys shouldn't be a problem but seems broken + if (psr.be) { + printf("*** DOMAIN TRYING TO TURN ON BIG-ENDIAN!!!\n"); + return (IA64_ILLOP_FAULT); + } + PSCB(vcpu).incomplete_regframe = 0; // is this necessary? + ifs = PSCB(vcpu).ifs; + //if ((ifs & regs->cr_ifs & 0x8000000000000000L) && ifs != regs->cr_ifs) { + //if ((ifs & 0x8000000000000000L) && ifs != regs->cr_ifs) { + if (ifs & regs->cr_ifs & 0x8000000000000000L) { +#define SI_OFS(x) ((char *)(&PSCB(vcpu).x) - (char *)(vcpu->shared_info)) +if (SI_OFS(iip)!=0x150 || SI_OFS(ipsr)!=0x148 || SI_OFS(ifs)!=0x158) { +printf("SI_CR_IIP/IPSR/IFS_OFFSET CHANGED, SEE dorfirfi\n"); +while(1); +} + // TODO: validate PSCB(vcpu).iip + // TODO: PSCB(vcpu).ipsr = psr; + PSCB(vcpu).ipsr = psr.i64; + // now set up the trampoline + regs->cr_iip = *(unsigned long *)dorfirfi; // function pointer!! + __asm__ __volatile ("mov %0=psr;;":"=r"(regspsr)::"memory"); + regs->cr_ipsr = regspsr & ~(IA64_PSR_I | IA64_PSR_IC | IA64_PSR_BN); + } + else { + regs->cr_ipsr = psr.i64; + regs->cr_iip = PSCB(vcpu).iip; + } + PSCB(vcpu).interrupt_collection_enabled = 1; + vcpu_bsw1(vcpu); + PSCB(vcpu).interrupt_delivery_enabled = int_enable; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_cover(VCPU *vcpu) +{ + REGS *regs = vcpu_regs(vcpu); + + if (!PSCB(vcpu).interrupt_collection_enabled) { + if (!PSCB(vcpu).incomplete_regframe) + PSCB(vcpu).ifs = regs->cr_ifs; + else PSCB(vcpu).incomplete_regframe = 0; + } + regs->cr_ifs = 0; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_thash(VCPU *vcpu, UINT64 vadr, UINT64 *pval) +{ + extern unsigned long vcpu_get_rr_ps(VCPU *vcpu,UINT64 vadr); + UINT64 pta = PSCB(vcpu).pta; + UINT64 pta_sz = (pta & IA64_PTA_SZ(0x3f)) >> IA64_PTA_SZ_BIT; + UINT64 pta_base = pta & ~((1UL << IA64_PTA_BASE_BIT)-1); + UINT64 Mask = (1L << pta_sz) - 1; + UINT64 Mask_60_15 = (Mask >> 15) & 0x3fffffffffff; + UINT64 compMask_60_15 = ~Mask_60_15; + //UINT64 rr_ps = RR_TO_PS(get_rr(vadr)); + UINT64 rr_ps = vcpu_get_rr_ps(vcpu,vadr); + UINT64 VHPT_offset = (vadr >> rr_ps) << 3; + UINT64 VHPT_addr1 = vadr & 0xe000000000000000L; + UINT64 VHPT_addr2a = + ((pta_base >> 15) & 0x3fffffffffff) & compMask_60_15; + UINT64 VHPT_addr2b = + ((VHPT_offset >> 15) & 0x3fffffffffff) & Mask_60_15;; + UINT64 VHPT_addr3 = VHPT_offset & 0x3fff; + UINT64 VHPT_addr = VHPT_addr1 | ((VHPT_addr2a | VHPT_addr2b) << 15) | + VHPT_addr3; + + if (VHPT_addr1 == 0xe000000000000000L) { + printf("vcpu_thash: thash unsupported with rr7 @%lx\n", + PSCB(vcpu).iip); + return (IA64_ILLOP_FAULT); + } +//verbose("vcpu_thash: vadr=%p, VHPT_addr=%p\n",vadr,VHPT_addr); + *pval = VHPT_addr; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_ttag(VCPU *vcpu, UINT64 vadr, UINT64 *padr) +{ + printf("vcpu_ttag: ttag instruction unsupported\n"); + return (IA64_ILLOP_FAULT); +} + +IA64FAULT vcpu_tpa(VCPU *vcpu, UINT64 vadr, UINT64 *padr) +{ + extern TR_ENTRY *match_tr(VCPU *,UINT64); + extern TR_ENTRY *match_dtlb(VCPU *,UINT64); + TR_ENTRY *trp; + UINT64 mask; + +extern unsigned long privop_trace; + if ((trp=match_tr(current,vadr)) || (trp=match_dtlb(current,vadr))) { + mask = (1L << trp->ps) - 1; + *padr = ((trp->ppn << 12) & ~mask) | (vadr & mask); + verbose("vcpu_tpa: addr=%p @%p, successful, padr=%p\n",vadr,PSCB(vcpu).iip,*padr); + return (IA64_NO_FAULT); + } + verbose("vcpu_tpa addr=%p, @%p, forcing data miss\n",vadr,PSCB(vcpu).iip); + return vcpu_force_data_miss(vcpu, vadr); +} + +IA64FAULT vcpu_tak(VCPU *vcpu, UINT64 vadr, UINT64 *key) +{ + printf("vcpu_tak: tak instruction unsupported\n"); + return (IA64_ILLOP_FAULT); + // HACK ALERT: tak does a thash for now + //return vcpu_thash(vcpu,vadr,key); +} + +/************************************************************************** + VCPU debug breakpoint register access routines +**************************************************************************/ + +IA64FAULT vcpu_set_dbr(VCPU *vcpu, UINT64 reg, UINT64 val) +{ + // TODO: unimplemented DBRs return a reserved register fault + // TODO: Should set Logical CPU state, not just physical + ia64_set_dbr(reg,val); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_ibr(VCPU *vcpu, UINT64 reg, UINT64 val) +{ + // TODO: unimplemented IBRs return a reserved register fault + // TODO: Should set Logical CPU state, not just physical + ia64_set_ibr(reg,val); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_dbr(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ + // TODO: unimplemented DBRs return a reserved register fault + UINT64 val = ia64_get_dbr(reg); + *pval = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_ibr(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ + // TODO: unimplemented IBRs return a reserved register fault + UINT64 val = ia64_get_ibr(reg); + *pval = val; + return (IA64_NO_FAULT); +} + +/************************************************************************** + VCPU performance monitor register access routines +**************************************************************************/ + +IA64FAULT vcpu_set_pmc(VCPU *vcpu, UINT64 reg, UINT64 val) +{ + // TODO: Should set Logical CPU state, not just physical + // NOTE: Writes to unimplemented PMC registers are discarded + ia64_set_pmc(reg,val); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_set_pmd(VCPU *vcpu, UINT64 reg, UINT64 val) +{ + // TODO: Should set Logical CPU state, not just physical + // NOTE: Writes to unimplemented PMD registers are discarded + ia64_set_pmd(reg,val); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_pmc(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ + // NOTE: Reads from unimplemented PMC registers return zero + UINT64 val = (UINT64)ia64_get_pmc(reg); + *pval = val; + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_pmd(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ + // NOTE: Reads from unimplemented PMD registers return zero + UINT64 val = (UINT64)ia64_get_pmd(reg); + *pval = val; + return (IA64_NO_FAULT); +} + +/************************************************************************** + VCPU banked general register access routines +**************************************************************************/ + +IA64FAULT vcpu_bsw0(VCPU *vcpu) +{ + REGS *regs = vcpu_regs(vcpu); + unsigned long *r = ®s->r16; + unsigned long *b0 = &PSCB(vcpu).bank0_regs[0]; + unsigned long *b1 = &PSCB(vcpu).bank1_regs[0]; + int i; + + if (PSCB(vcpu).banknum) { + for (i = 0; i < 16; i++) { *b1++ = *r; *r++ = *b0++; } + PSCB(vcpu).banknum = 0; + } + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_bsw1(VCPU *vcpu) +{ + REGS *regs = vcpu_regs(vcpu); + unsigned long *r = ®s->r16; + unsigned long *b0 = &PSCB(vcpu).bank0_regs[0]; + unsigned long *b1 = &PSCB(vcpu).bank1_regs[0]; + int i; + + if (!PSCB(vcpu).banknum) { + for (i = 0; i < 16; i++) { *b0++ = *r; *r++ = *b1++; } + PSCB(vcpu).banknum = 1; + } + return (IA64_NO_FAULT); +} + +/************************************************************************** + VCPU cpuid access routines +**************************************************************************/ + + +IA64FAULT vcpu_get_cpuid(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ + // FIXME: This could get called as a result of a rsvd-reg fault + // if reg > 3 + switch(reg) { + case 0: + case 1: + memcpy(pval,"Xen/ia64",8); + break; + case 2: + *pval = 0; + break; + case 3: + *pval = 0; //FIXME: See vol1, 3.1.11 + break; + case 4: + *pval = 1; //FIXME: See vol1, 3.1.11 + break; + default: + *pval = 0; //FIXME: See vol1, 3.1.11 + break; + } + return (IA64_NO_FAULT); +} + +/************************************************************************** + VCPU region register access routines +**************************************************************************/ + +unsigned long vcpu_get_rr_ve(VCPU *vcpu,UINT64 vadr) +{ + + ia64_rr rr; + + rr.rrval = PSCB(vcpu).rrs[vadr>>61]; + return(rr.ve); +} + + +unsigned long vcpu_get_rr_ps(VCPU *vcpu,UINT64 vadr) +{ + + ia64_rr rr; + + rr.rrval = PSCB(vcpu).rrs[vadr>>61]; + return(rr.ps); +} + + +unsigned long vcpu_get_rr_rid(VCPU *vcpu,UINT64 vadr) +{ + + ia64_rr rr; + + rr.rrval = PSCB(vcpu).rrs[vadr>>61]; + return(rr.rid); +} + + +IA64FAULT vcpu_set_rr(VCPU *vcpu, UINT64 reg, UINT64 val) +{ + extern void set_one_rr(UINT64, UINT64); + PSCB(vcpu).rrs[reg>>61] = val; + // warning: set_one_rr() does it "live" + set_one_rr(reg,val); + return (IA64_NO_FAULT); +} + +IA64FAULT vcpu_get_rr(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ + UINT val = PSCB(vcpu).rrs[reg>>61]; + *pval = val; + return (IA64_NO_FAULT); +} + +/************************************************************************** + VCPU protection key register access routines +**************************************************************************/ + +IA64FAULT vcpu_get_pkr(VCPU *vcpu, UINT64 reg, UINT64 *pval) +{ +#ifndef PKR_USE_FIXED + printk("vcpu_get_pkr: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +#else + UINT64 val = (UINT64)ia64_get_pkr(reg); + *pval = val; + return (IA64_NO_FAULT); +#endif +} + +IA64FAULT vcpu_set_pkr(VCPU *vcpu, UINT64 reg, UINT64 val) +{ +#ifndef PKR_USE_FIXED + printk("vcpu_set_pkr: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +#else +// if (reg >= NPKRS) return (IA64_ILLOP_FAULT); + vcpu->pkrs[reg] = val; + ia64_set_pkr(reg,val); + return (IA64_NO_FAULT); +#endif +} + +/************************************************************************** + VCPU translation register access routines +**************************************************************************/ + +static void vcpu_purge_tr_entry(TR_ENTRY *trp) +{ + trp->p = 0; +} + +static void vcpu_set_tr_entry(TR_ENTRY *trp, UINT64 pte, UINT64 itir, UINT64 ifa) +{ + UINT64 ps; + + trp->itir = itir; + trp->rid = virtualize_rid(current, get_rr(ifa) & RR_RID_MASK); + trp->p = 1; + ps = trp->ps; + trp->page_flags = pte; + if (trp->pl < 2) trp->pl = 2; + trp->vadr = ifa & ~0xfff; + if (ps > 12) { // "ignore" relevant low-order bits + trp->ppn &= ~((1UL<<(ps-12))-1); + trp->vadr &= ~((1UL<p) continue; + if (physicalize_rid(vcpu,trp->rid) != rid) continue; + if (ifa < trp->vadr) continue; + if (ifa >= (trp->vadr + (1L << trp->ps)) - 1) continue; + //if (trp->key && !match_pkr(vcpu,trp->key)) continue; + return trp; + } + return 0; +} + +TR_ENTRY *match_tr(VCPU *vcpu, unsigned long ifa) +{ + TR_ENTRY *trp; + + trp = vcpu_match_tr_entry(vcpu,vcpu->shared_info->arch.dtrs,ifa,NDTRS); + if (trp) return trp; + trp = vcpu_match_tr_entry(vcpu,vcpu->shared_info->arch.itrs,ifa,NITRS); + if (trp) return trp; + return 0; +} + +IA64FAULT vcpu_itr_d(VCPU *vcpu, UINT64 slot, UINT64 pte, + UINT64 itir, UINT64 ifa) +{ + TR_ENTRY *trp; + + if (slot >= NDTRS) return IA64_RSVDREG_FAULT; + trp = &PSCB(vcpu).dtrs[slot]; + vcpu_set_tr_entry(trp,pte,itir,ifa); + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_itr_i(VCPU *vcpu, UINT64 slot, UINT64 pte, + UINT64 itir, UINT64 ifa) +{ + TR_ENTRY *trp; + + if (slot >= NITRS) return IA64_RSVDREG_FAULT; + trp = &PSCB(vcpu).itrs[slot]; + vcpu_set_tr_entry(trp,pte,itir,ifa); + return IA64_NO_FAULT; +} + +/************************************************************************** + VCPU translation cache access routines +**************************************************************************/ + +void foobar(void) { /*vcpu_verbose = 1;*/ } + +extern VCPU *dom0; + +void vcpu_itc_no_srlz(VCPU *vcpu, UINT64 IorD, UINT64 vaddr, UINT64 pte, UINT64 logps) +{ + unsigned long psr; + unsigned long ps = (vcpu==dom0) ? logps : PAGE_SHIFT; + + // FIXME: validate ifa here (not in Xen space), COULD MACHINE CHECK! + // FIXME, must be inlined or potential for nested fault here! + psr = ia64_clear_ic(); + ia64_itc(IorD,vaddr,pte,ps); // FIXME: look for bigger mappings + ia64_set_psr(psr); + // ia64_srlz_i(); // no srls req'd, will rfi later + if (IorD & 0x1) vcpu_set_tr_entry(&PSCB(vcpu).itlb,pte,logps<<2,vaddr); + if (IorD & 0x2) vcpu_set_tr_entry(&PSCB(vcpu).dtlb,pte,logps<<2,vaddr); +} + +TR_ENTRY *match_dtlb(VCPU *vcpu, unsigned long ifa) +{ + return vcpu_match_tr_entry(vcpu,&vcpu->shared_info->arch.dtlb,ifa,1); +} + +IA64FAULT vcpu_itc_d(VCPU *vcpu, UINT64 pte, UINT64 itir, UINT64 ifa) +{ + unsigned long pteval, logps = (itir >> 2) & 0x3f; + unsigned long translate_domain_pte(UINT64,UINT64,UINT64); + + if (((itir & ~0xfc) >> 2) < PAGE_SHIFT) { + printf("vcpu_itc_d: domain trying to use smaller page size!\n"); + //FIXME: kill domain here + while(1); + } + //itir = (itir & ~0xfc) | (PAGE_SHIFT<<2); // ignore domain's pagesize + pteval = translate_domain_pte(pte,ifa,itir); + if (!pteval) return IA64_ILLOP_FAULT; + vcpu_itc_no_srlz(vcpu,2,ifa,pteval,logps); + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_itc_i(VCPU *vcpu, UINT64 pte, UINT64 itir, UINT64 ifa) +{ + unsigned long pteval, logps = (itir >> 2) & 0x3f; + unsigned long translate_domain_pte(UINT64,UINT64,UINT64); + + // FIXME: validate ifa here (not in Xen space), COULD MACHINE CHECK! + if (((itir & ~0xfc) >> 2) < PAGE_SHIFT) { + printf("vcpu_itc_i: domain trying to use smaller page size!\n"); + //FIXME: kill domain here + while(1); + } + //itir = (itir & ~0xfc) | (PAGE_SHIFT<<2); // ignore domain's pagesize + pteval = translate_domain_pte(pte,ifa,itir); + // FIXME: what to do if bad physical address? (machine check?) + if (!pteval) return IA64_ILLOP_FAULT; + vcpu_itc_no_srlz(vcpu, 1,ifa,pteval,logps); + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_ptc_l(VCPU *vcpu, UINT64 vadr, UINT64 addr_range) +{ + printk("vcpu_ptc_l: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +} + +IA64FAULT vcpu_fc(VCPU *vcpu, UINT64 vadr) +{ + UINT64 mpaddr; + IA64FAULT fault; + unsigned long lookup_domain_mpa(VCPU *,unsigned long); + unsigned long pteval, dom_imva; + + fault = vcpu_tpa(vcpu, vadr, &mpaddr); + if (fault == IA64_NO_FAULT) { + struct domain *dom0; + unsigned long dom0_start, dom0_size; + if (vcpu == dom0) { + if (mpaddr < dom0_start || mpaddr >= dom0_start + dom0_size) { + printk("vcpu_fc: bad dom0 mpaddr %p!\n",mpaddr); + } + } + pteval = lookup_domain_mpa(vcpu,mpaddr); + if (pteval) { + dom_imva = __va(pteval & _PFN_MASK); + ia64_fc(dom_imva); + } + else { + REGS *regs = vcpu_regs(vcpu); + printk("vcpu_fc: can't flush vadr=%p, iip=%p\n", + vadr,regs->cr_iip); + } + } + return fault; +} + +IA64FAULT vcpu_ptc_e(VCPU *vcpu, UINT64 vadr) +{ + + // Note that this only needs to be called once, i.e. the + // architected loop to purge the entire TLB, should use + // base = stride1 = stride2 = 0, count0 = count 1 = 1 + + // FIXME: When VHPT is in place, flush that too! + local_flush_tlb_all(); + // just invalidate the "whole" tlb + vcpu_purge_tr_entry(&PSCB(vcpu).dtlb); + vcpu_purge_tr_entry(&PSCB(vcpu).itlb); + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_ptc_g(VCPU *vcpu, UINT64 vadr, UINT64 addr_range) +{ + printk("vcpu_ptc_g: called, not implemented yet\n"); + return IA64_ILLOP_FAULT; +} + +IA64FAULT vcpu_ptc_ga(VCPU *vcpu,UINT64 vadr,UINT64 addr_range) +{ + extern ia64_global_tlb_purge(UINT64 start, UINT64 end, UINT64 nbits); + // FIXME: validate not flushing Xen addresses + // if (Xen address) return(IA64_ILLOP_FAULT); + // FIXME: ??breaks if domain PAGE_SIZE < Xen PAGE_SIZE + ia64_global_tlb_purge(vadr,vadr+addr_range,PAGE_SHIFT); + vcpu_purge_tr_entry(&PSCB(vcpu).dtlb); + vcpu_purge_tr_entry(&PSCB(vcpu).itlb); + return IA64_NO_FAULT; +} + +IA64FAULT vcpu_ptr_d(VCPU *vcpu,UINT64 vadr,UINT64 addr_range) +{ + printf("vcpu_ptr_d: Purging TLB is unsupported\n"); + return (IA64_ILLOP_FAULT); +} + +IA64FAULT vcpu_ptr_i(VCPU *vcpu,UINT64 vadr,UINT64 addr_range) +{ + printf("vcpu_ptr_i: Purging TLB is unsupported\n"); + return (IA64_ILLOP_FAULT); +} + +void vcpu_set_regs(VCPU *vcpu, REGS *regs) +{ + vcpu->regs = regs; +} diff --git a/xen/arch/ia64/xenasm.S b/xen/arch/ia64/xenasm.S new file mode 100644 index 0000000000..8616e078eb --- /dev/null +++ b/xen/arch/ia64/xenasm.S @@ -0,0 +1,461 @@ +/* + * Assembly support routines for Xen/ia64 + * + * Copyright (C) 2004 Hewlett-Packard Co + * Dan Magenheimer + */ + +#include +#include +#include +#include +#include + +#define RunningOnHpSki(rx,ry,pn) \ + addl rx = 2, r0; \ + addl ry = 3, r0; \ + ;; \ + mov rx = cpuid[rx]; \ + mov ry = cpuid[ry]; \ + ;; \ + cmp.eq pn,p0 = 0, rx; \ + ;; \ + (pn) movl rx = 0x7000004 ; \ + ;; \ + (pn) cmp.eq pn,p0 = ry, rx; \ + ;; + +//int platform_is_hp_ski(void) +GLOBAL_ENTRY(platform_is_hp_ski) + mov r8 = 0 + RunningOnHpSki(r3,r9,p8) +(p8) mov r8 = 1 + br.ret.sptk.many b0 +END(platform_is_hp_ski) + +// Change rr7 to the passed value while ensuring +// Xen is mapped into the new region +#define PSR_BITS_TO_CLEAR \ + (IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT | \ + IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \ + IA64_PSR_DFL | IA64_PSR_DFH) +// FIXME? Note that this turns off the DB bit (debug) +#define PSR_BITS_TO_SET IA64_PSR_BN + +GLOBAL_ENTRY(ia64_new_rr7) + // not sure this unwind statement is correct... + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(1) + alloc loc1 = ar.pfs, 1, 7, 0, 0 +1: { + mov r28 = in0 // copy procedure index + mov r8 = ip // save ip to compute branch + mov loc0 = rp // save rp + };; + .body + movl loc2=PERCPU_ADDR + ;; + tpa loc2=loc2 // grab this BEFORE changing rr7 + ;; +#if VHPT_ENABLED + movl loc6=VHPT_ADDR + ;; + tpa loc6=loc6 // grab this BEFORE changing rr7 + ;; +#endif + movl loc5=SHAREDINFO_ADDR + ;; + tpa loc5=loc5 // grab this BEFORE changing rr7 + ;; + mov loc3 = psr // save psr + adds r8 = 1f-1b,r8 // calculate return address for call + ;; + tpa r8=r8 // convert rp to physical + ;; + mov loc4=ar.rsc // save RSE configuration + ;; + mov ar.rsc=0 // put RSE in enforced lazy, LE mode + movl r16=PSR_BITS_TO_CLEAR + movl r17=PSR_BITS_TO_SET + ;; + or loc3=loc3,r17 // add in psr the bits to set + ;; + andcm r16=loc3,r16 // removes bits to clear from psr + br.call.sptk.many rp=ia64_switch_mode_phys +1: + // now in physical mode with psr.i/ic off so do rr7 switch + dep r16=-1,r0,61,3 + ;; + mov rr[r16]=in0 + srlz.d + ;; + + // re-pin mappings for kernel text and data + mov r18=KERNEL_TR_PAGE_SHIFT<<2 + movl r17=KERNEL_START + ;; + rsm psr.i | psr.ic + ;; + srlz.i + ;; + ptr.i r17,r18 + ptr.d r17,r18 + ;; + mov cr.itir=r18 + mov cr.ifa=r17 + mov r16=IA64_TR_KERNEL + //mov r3=ip + movl r18=PAGE_KERNEL + ;; + dep r2=0,r3,0,KERNEL_TR_PAGE_SHIFT + ;; + or r18=r2,r18 + ;; + srlz.i + ;; + itr.i itr[r16]=r18 + ;; + itr.d dtr[r16]=r18 + ;; + + // re-pin mappings for stack (current), per-cpu, vhpt, and shared info + + // unless overlaps with KERNEL_TR + dep r18=0,r13,0,KERNEL_TR_PAGE_SHIFT + ;; + cmp.eq p7,p0=r17,r18 +(p7) br.cond.sptk .stack_overlaps + ;; + movl r25=PAGE_KERNEL + dep r20=0,r13,50,14 // physical address of "current" + ;; + or r23=r25,r20 // construct PA | page properties + mov r25=IA64_GRANULE_SHIFT<<2 + ;; + ptr.d r13,r25 + ;; + mov cr.itir=r25 + mov cr.ifa=r13 // VA of next task... + ;; + mov r25=IA64_TR_CURRENT_STACK + ;; + itr.d dtr[r25]=r23 // wire in new mapping... + ;; +.stack_overlaps: + + movl r22=PERCPU_ADDR + ;; + movl r25=PAGE_KERNEL + ;; + mov r20=loc2 // saved percpu physical address + ;; + or r23=r25,r20 // construct PA | page properties + mov r24=PERCPU_PAGE_SHIFT<<2 + ;; + ptr.d r22,r24 + ;; + mov cr.itir=r24 + mov cr.ifa=r22 + ;; + mov r25=IA64_TR_PERCPU_DATA + ;; + itr.d dtr[r25]=r23 // wire in new mapping... + ;; + +#if VHPT_ENABLED + movl r22=VHPT_ADDR + ;; + movl r25=PAGE_KERNEL + ;; + mov r20=loc6 // saved vhpt physical address + ;; + or r23=r25,r20 // construct PA | page properties + mov r24=VHPT_PAGE_SHIFT<<2 + ;; + ptr.d r22,r24 + ;; + mov cr.itir=r24 + mov cr.ifa=r22 + ;; + mov r25=IA64_TR_VHPT + ;; + itr.d dtr[r25]=r23 // wire in new mapping... + ;; +#endif + + movl r22=SHAREDINFO_ADDR + ;; + movl r25=PAGE_KERNEL + ;; + mov r20=loc5 // saved sharedinfo physical address + ;; + or r23=r25,r20 // construct PA | page properties + mov r24=PAGE_SHIFT<<2 + ;; + ptr.d r22,r24 + ;; + mov cr.itir=r24 + mov cr.ifa=r22 + ;; + mov r25=IA64_TR_SHARED_INFO + ;; + itr.d dtr[r25]=r23 // wire in new mapping... + ;; + + // done, switch back to virtual and return + mov r16=loc3 // r16= original psr + br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode + mov psr.l = loc3 // restore init PSR + + mov ar.pfs = loc1 + mov rp = loc0 + ;; + mov ar.rsc=loc4 // restore RSE configuration + srlz.d // seralize restoration of psr.l + br.ret.sptk.many rp +END(ia64_new_rr7) + +#include "minstate.h" + +GLOBAL_ENTRY(ia64_prepare_handle_privop) + .prologue + /* + * r16 = fake ar.pfs, we simply need to make sure privilege is still 0 + */ + mov r16=r0 + DO_SAVE_SWITCH_STACK + br.call.sptk.many rp=ia64_handle_privop // stack frame setup in ivt +.ret22: .body + DO_LOAD_SWITCH_STACK + br.cond.sptk.many rp // goes to ia64_leave_kernel +END(ia64_prepare_handle_privop) + +GLOBAL_ENTRY(ia64_prepare_handle_break) + .prologue + /* + * r16 = fake ar.pfs, we simply need to make sure privilege is still 0 + */ + mov r16=r0 + DO_SAVE_SWITCH_STACK + br.call.sptk.many rp=ia64_handle_break // stack frame setup in ivt +.ret23: .body + DO_LOAD_SWITCH_STACK + br.cond.sptk.many rp // goes to ia64_leave_kernel +END(ia64_prepare_handle_break) + +GLOBAL_ENTRY(ia64_prepare_handle_reflection) + .prologue + /* + * r16 = fake ar.pfs, we simply need to make sure privilege is still 0 + */ + mov r16=r0 + DO_SAVE_SWITCH_STACK + br.call.sptk.many rp=ia64_handle_reflection // stack frame setup in ivt +.ret24: .body + DO_LOAD_SWITCH_STACK + br.cond.sptk.many rp // goes to ia64_leave_kernel +END(ia64_prepare_handle_reflection) + +// NOTE: instruction spacing must be explicit for recovery on miss +GLOBAL_ENTRY(__get_domain_bundle) + ld8 r8=[r32],8 + nop 0 + nop 0 + ;; + ld8 r9=[r32] + nop 0 + nop 0 + ;; + br.ret.sptk.many rp + nop 0 + nop 0 + ;; +END(__get_domain_bundle) + +GLOBAL_ENTRY(dorfirfi) +#define SI_CR_IIP_OFFSET 0x150 +#define SI_CR_IPSR_OFFSET 0x148 +#define SI_CR_IFS_OFFSET 0x158 + movl r16 = SHAREDINFO_ADDR+SI_CR_IIP_OFFSET + movl r17 = SHAREDINFO_ADDR+SI_CR_IPSR_OFFSET + movl r18 = SHAREDINFO_ADDR+SI_CR_IFS_OFFSET + ;; + ld8 r16 = [r16] + ld8 r17 = [r17] + ld8 r18 = [r18] + ;; + mov cr.iip=r16 + mov cr.ipsr=r17 + mov cr.ifs=r18 + ;; + // fall through +END(dorfirfi) + +GLOBAL_ENTRY(dorfi) + rfi + ;; +END(dorfirfi) + +// +// Long's Peak UART Offsets +// +#define COM_TOP 0xff5e0000 +#define COM_BOT 0xff5e2000 + +// UART offsets +#define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */ +#define UART_INT_ENB 1 /* interrupt enable (DLAB=0) */ +#define UART_INT_ID 2 /* Interrupt ID register */ +#define UART_LINE_CTL 3 /* Line control register */ +#define UART_MODEM_CTL 4 /* Modem Control Register */ +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_MSR 6 /* Modem status register */ +#define UART_DLATCH_LOW UART_TX +#define UART_DLATCH_HIGH UART_INT_ENB +#define COM1 0x3f8 +#define COM2 0x2F8 +#define COM3 0x3E8 + +/* interrupt enable bits (offset 1) */ +#define DATA_AVAIL_INT 1 +#define XMIT_HOLD_EMPTY_INT 2 +#define LINE_STAT_INT 4 +#define MODEM_STAT_INT 8 + +/* line status bits (offset 5) */ +#define REC_DATA_READY 1 +#define OVERRUN 2 +#define PARITY_ERROR 4 +#define FRAMING_ERROR 8 +#define BREAK_INTERRUPT 0x10 +#define XMIT_HOLD_EMPTY 0x20 +#define XMIT_SHIFT_EMPTY 0x40 + +// Write a single character +// input: r32 = character to be written +// output: none +GLOBAL_ENTRY(longs_peak_putc) + rsm psr.dt + movl r16 = 0x8000000000000000 + COM_TOP + UART_LSR + ;; + srlz.i + ;; + +.Chk_THRE_p: + ld1.acq r18=[r16] + ;; + + and r18 = XMIT_HOLD_EMPTY, r18 + ;; + cmp4.eq p6,p0=0,r18 + ;; + +(p6) br .Chk_THRE_p + ;; + movl r16 = 0x8000000000000000 + COM_TOP + UART_TX + ;; + st1.rel [r16]=r32 + ;; + ssm psr.dt + ;; + srlz.i + ;; + br.ret.sptk.many b0 +END(longs_peak_putc) + +/* derived from linux/arch/ia64/hp/sim/boot/boot_head.S */ +GLOBAL_ENTRY(pal_emulator_static) + mov r8=-1 + mov r9=256 + ;; + cmp.gtu p7,p8=r9,r32 /* r32 <= 255? */ +(p7) br.cond.sptk.few static + ;; + mov r9=512 + ;; + cmp.gtu p7,p8=r9,r32 +(p7) br.cond.sptk.few stacked + ;; +static: cmp.eq p7,p8=6,r32 /* PAL_PTCE_INFO */ +(p8) br.cond.sptk.few 1f + ;; + mov r8=0 /* status = 0 */ + movl r9=0x100000000 /* tc.base */ + movl r10=0x0000000200000003 /* count[0], count[1] */ + movl r11=0x1000000000002000 /* stride[0], stride[1] */ + br.ret.sptk.few rp +1: cmp.eq p7,p8=14,r32 /* PAL_FREQ_RATIOS */ +(p8) br.cond.sptk.few 1f + mov r8=0 /* status = 0 */ + movl r9 =0x900000002 /* proc_ratio (1/100) */ + movl r10=0x100000100 /* bus_ratio<<32 (1/256) */ + movl r11=0x900000002 /* itc_ratio<<32 (1/100) */ + ;; +1: cmp.eq p7,p8=19,r32 /* PAL_RSE_INFO */ +(p8) br.cond.sptk.few 1f + mov r8=0 /* status = 0 */ + mov r9=96 /* num phys stacked */ + mov r10=0 /* hints */ + mov r11=0 + br.ret.sptk.few rp +1: cmp.eq p7,p8=1,r32 /* PAL_CACHE_FLUSH */ +(p8) br.cond.sptk.few 1f +#if 0 + mov r9=ar.lc + movl r8=524288 /* flush 512k million cache lines (16MB) */ + ;; + mov ar.lc=r8 + movl r8=0xe000000000000000 + ;; +.loop: fc r8 + add r8=32,r8 + br.cloop.sptk.few .loop + sync.i + ;; + srlz.i + ;; + mov ar.lc=r9 + mov r8=r0 + ;; +1: cmp.eq p7,p8=15,r32 /* PAL_PERF_MON_INFO */ +(p8) br.cond.sptk.few 1f + mov r8=0 /* status = 0 */ + movl r9 =0x08122f04 /* generic=4 width=47 retired=8 cycles=18 */ + mov r10=0 /* reserved */ + mov r11=0 /* reserved */ + mov r16=0xffff /* implemented PMC */ + mov r17=0x3ffff /* implemented PMD */ + add r18=8,r29 /* second index */ + ;; + st8 [r29]=r16,16 /* store implemented PMC */ + st8 [r18]=r0,16 /* clear remaining bits */ + ;; + st8 [r29]=r0,16 /* clear remaining bits */ + st8 [r18]=r0,16 /* clear remaining bits */ + ;; + st8 [r29]=r17,16 /* store implemented PMD */ + st8 [r18]=r0,16 /* clear remaining bits */ + mov r16=0xf0 /* cycles count capable PMC */ + ;; + st8 [r29]=r0,16 /* clear remaining bits */ + st8 [r18]=r0,16 /* clear remaining bits */ + mov r17=0xf0 /* retired bundles capable PMC */ + ;; + st8 [r29]=r16,16 /* store cycles capable */ + st8 [r18]=r0,16 /* clear remaining bits */ + ;; + st8 [r29]=r0,16 /* clear remaining bits */ + st8 [r18]=r0,16 /* clear remaining bits */ + ;; + st8 [r29]=r17,16 /* store retired bundle capable */ + st8 [r18]=r0,16 /* clear remaining bits */ + ;; + st8 [r29]=r0,16 /* clear remaining bits */ + st8 [r18]=r0,16 /* clear remaining bits */ + ;; +1: br.cond.sptk.few rp +#else +1: +#endif +stacked: + br.ret.sptk.few rp +END(pal_emulator_static) -- 2.30.2